
com.google.gwt.inject.rebind.resolution.DependencyGraph Maven / Gradle / Ivy
/*
* 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