All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.inject.rebind.resolution.PathFinder 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.inject.rebind.binding.Dependency;
import com.google.gwt.inject.rebind.util.Preconditions;
import com.google.inject.Key;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

/**
 * Finds the shortest path from the edges in the root set to any of one or more destination keys.
 * 
 * 

This is used in {@link EagerCycleFinder} and {@link UnresolvedBindingValidator} for explaining * why a given error/cycle was reachable from the Ginjector. */ public class PathFinder { private DependencyGraph graph; private Collection> destinations = new LinkedHashSet>(); private Collection> roots = new LinkedHashSet>(); private boolean onlyRequiredEdges; /** * For every key that is discovered during the Breadth-first search, this points to the edge that * discovered it. We don't need to revisit(or requeue) any keys that are already defined here, * because any new edge leading to back to the given key will at best be longer than the current * path. */ private Map, Dependency> visited; /** * The nodes remaining to visit. */ private Queue> workQueue; public PathFinder() {} public PathFinder onGraph(DependencyGraph graph) { this.graph = graph; return this; } public PathFinder addRoots(Key... roots) { Collections.addAll(this.roots, roots); return this; } /** * Add destinations to be used for the next search. The shortest path from the unresolved set * to any member of the destination set will be returned from {@link #findShortestPath()}. */ public PathFinder addDestinations(Key... destinations) { Collections.addAll(this.destinations, destinations); return this; } /** * @param onlyRequiredEdges if true, only required edges will be considered when searching for the * path */ public PathFinder withOnlyRequiredEdges(boolean onlyRequiredEdges) { this.onlyRequiredEdges = onlyRequiredEdges; return this; } /** * Find the shortest path from an unresolved edge in the roots to a key in the destinations. * *

Implemented as a Breadth-first search from the destination set back to the origin. * * @return the shortest path from the roots to any of the destinations specified that passes * through edges meeting the criteria; can be empty if destination is already in the root * set, or null if no path exists */ public List findShortestPath() { Preconditions.checkNotNull(graph, "Must call onGraph(DependencyGraph) before findShortestPath"); Preconditions.checkState(!roots.isEmpty(), "Must call addRoots(Key...) before findShortestPath"); Preconditions.checkState(!destinations.isEmpty(), "Must call addDestinations(Key...) before findShortestPath"); visited = new LinkedHashMap, Dependency>(); workQueue = new LinkedList>(); // Populate the workqueue with our initial destination keys. If any of them are in the root // set, we can return early. for (Key key : destinations) { visited.put(key, null); if (roots.contains(key)) { return getPathFor(key); } workQueue.add(key); } // Perform a BFS looking for a path back to a root edge while (!workQueue.isEmpty()) { Key key = workQueue.remove(); for (Dependency edge : graph.getDependenciesTargeting(key)) { if (isEdgeUsable(edge)) { Key sourceKey = edge.getSource(); if (!visited.containsKey(sourceKey)) { workQueue.add(sourceKey); visited.put(sourceKey, edge); // Check for early termination if (roots.contains(sourceKey)) { return getPathFor(sourceKey); } } } } } // Shouldn't be possible, unless the only paths reaching the destinations take optional edges // and requiredOnly is true. return null; } private List getPathFor(Key rootKey) { List result = new ArrayList(); // Now, add the edges from the BFS path Dependency edge = visited.get(rootKey); while (edge != null) { result.add(edge); edge = visited.get(edge.getTarget()); } return result; } /** * Returns true if the given edge meets our criteria for use, false otherwise. */ private boolean isEdgeUsable(Dependency edge) { return !edge.isOptional() || !onlyRequiredEdges; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy