
com.google.gwt.inject.rebind.resolution.DependencyGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gin Show documentation
Show all versions of gin Show documentation
GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.inject.rebind.resolution;
import com.google.gwt.dev.util.Preconditions;
import com.google.gwt.inject.rebind.GinjectorBindings;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.Dependency;
import com.google.inject.Key;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* A graph of the dependency information between types that need to be injected
* at a given injector, called the origin.
*
*
* A {@link DependencyGraph} consists of a set of dependencies linking
* dependency nodes (keys). Each edge explains how a particular node came to be
* required: for instance, a key may be required in the {@code @Inject}
* constructor of a class, or it might be required by the Ginjector. There are
* two kinds of dependency nodes:
*
* - Pre-existing dependency nodes contain a {@link Key} and a
* {@link GinjectorBindings}. They represent keys that are already available on
* some Ginjector in the hierarchy and visible to the origin.
* - Internal dependency nodes represent bindings that are not already
* created/available, but that can have implicit bindings created for them. They
* contain the {@link Key} that the binding is for and the {@link Binding} that
* can be created.
* - In addition, some nodes may represent errors. These contain a {@link Key}
* and a {@link String} describing the error. These nodes are created for keys
* that do not already exist and for which we cannot create an implicit binding.
*
*
*/
public class DependencyGraph {
/**
* For each key, this stores the set of {@link Dependency}s that have the key as their source.
*/
private final Map, Set> dependenciesOf;
/**
* For each key, this stores the set of {@link Dependency}s that have the key as their target.
*/
private final Map, Set> dependenciesTargeting;
private final GinjectorBindings origin;
private DependencyGraph(GinjectorBindings origin, Map, Set> dependenciesOf,
Map, Set> dependenciesTargeting) {
this.origin = origin;
this.dependenciesOf = dependenciesOf;
this.dependenciesTargeting = dependenciesTargeting;
}
public int size() {
return dependenciesTargeting.size();
}
public GinjectorBindings getOrigin() {
return origin;
}
public Collection getDependenciesOf(Key> key) {
Set dependencySet = dependenciesOf.get(key);
return dependencySet == null ? Collections.emptyList()
: Collections.unmodifiableCollection(dependencySet);
}
public Collection getDependenciesTargeting(Key> key) {
Collection dependencySet = dependenciesTargeting.get(key);
return dependencySet == null ? Collections.emptyList()
: Collections.unmodifiableCollection(dependencySet);
}
/**
* Returns all the keys that appear in the Dependency Graph, other than the "common root",
* {@link Dependency#GINJECTOR}.
*/
public Iterable> getAllKeys() {
// All keys in the graph should be reachable from the Ginjector, which means they must appear as
// the target of some dependency. Thus, the keyset covers all nodes.
return dependenciesTargeting.keySet();
}
public static class Builder {
private final Map, Set> dependenciesOf;
private final Map, Set> dependenciesTargeting;
private final GinjectorBindings origin;
/**
* Creates a Builder that constructs a new DependencyGraph for the given origin Ginjector.
*
* @param origin the origin Ginjector
*/
public Builder(GinjectorBindings origin) {
this.origin = origin;
// Use linked hash maps so that error messages (and tests) are stable
this.dependenciesOf = new LinkedHashMap, Set>();
this.dependenciesTargeting = new LinkedHashMap, Set>();
}
public Builder addEdge(Dependency dependency) {
addTo(dependency.getSource(), dependency, dependenciesOf);
addTo(dependency.getTarget(), dependency, dependenciesTargeting);
return this;
}
private void addTo(
Key> key, Dependency dependency, Map, Set> dependencies) {
Set dependencySet = dependencies.get(key);
if (dependencySet == null) {
// Use LinkedHashSet so that error messages (and tests) are stable
dependencySet = new LinkedHashSet();
dependencies.put(key, dependencySet);
}
dependencySet.add(dependency);
}
public DependencyGraph build() {
return new DependencyGraph(origin, dependenciesOf, dependenciesTargeting);
}
}
public static class GraphPruner {
private final DependencyGraph source;
/**
* Create a {@link GraphPruner} for building a new DependencyGraph by (destructively!) removing
* edges from an existing DependencyGraph.
*
* @param source the DependencyGraph to use as the base (will be mutated)
*/
public GraphPruner(DependencyGraph source) {
this.source = source;
}
/**
* Removes the given key, all its incoming edges, and all its outgoing edges, from the graph.
*/
public GraphPruner remove(Key> key) {
for (Dependency dependency : source.getDependenciesOf(key)) {
removeFrom(dependency.getTarget(), dependency, source.dependenciesTargeting);
}
for (Dependency dependency : source.getDependenciesTargeting(key)) {
removeFrom(dependency.getSource(), dependency, source.dependenciesOf);
}
return this;
}
private void removeFrom(Key> key, Dependency dependency,
Map, Set> dependencies) {
Set dependencySet = dependencies.get(key);
Preconditions.checkNotNull(dependencySet,
"Expected dependency set to be present for dependency %s", key);
if (!dependencySet.remove(dependency)) {
throw new IllegalStateException(String.format(
"Expected %s to be present in the dependency set", dependency));
}
if (dependencySet.isEmpty()) {
dependencies.remove(key);
}
}
public DependencyGraph update() {
return source;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy