Package com.google.gwt.inject.rebind.resolution

Source Code of com.google.gwt.inject.rebind.resolution.PathFinder

/*
* 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.
*
* <p>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<Key<?>> destinations = new LinkedHashSet<Key<?>>();
  private Collection<Key<?>> roots = new LinkedHashSet<Key<?>>();
  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<Key<?>, Dependency> visited;
 
  /**
   * The nodes remaining to visit.
   */
  private Queue<Key<?>> 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.
   *
   * <p>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<Dependency> 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<Key<?>, Dependency>();
    workQueue = new LinkedList<Key<?>>();
   
    // 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<Dependency> getPathFor(Key<?> rootKey) {
    List<Dependency> result = new ArrayList<Dependency>();
   
    // 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;
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.resolution.PathFinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.