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

Source Code of com.google.gwt.inject.rebind.resolution.UnresolvedBindingValidator$Factory

/*
* 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.core.ext.TreeLogger;
import com.google.gwt.inject.rebind.ErrorManager;
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.gwt.inject.rebind.binding.ParentBinding;
import com.google.gwt.inject.rebind.resolution.DependencyExplorer.DependencyExplorerOutput;
import com.google.gwt.inject.rebind.util.Preconditions;
import com.google.gwt.inject.rebind.util.PrettyPrinter;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.assistedinject.Assisted;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* Finds and reports errors in the dependency information.  Removes all optional bindings that can't
* be constructed.
*
* <p>A key is required if there exists a path from a key in the unresolved set of the origin
* Ginjector to the key in question that passes only through required dependencies.  A key is
* optional if every path that leads to the key contains an optional dependency.
*
* This detects the following:
* <ul>
* <li>Circular dependencies that do not pass through a Provider or an AsyncProvider.  This is a
* fatal error, even for optional keys.
* </li>
* <li>Bindings that cannot be created without causing a double-binding.  Specifically, bindings for
* which the key is already bound below, but not exposed to, the Ginjector that needs the key.  This
* is only an error for required keys; optional keys are removed.
* </li>
* <li>Bindings that cannot have implicit dependencies created for them.  This is only an error if
* the key is required; optional keys are removed.
* </li>
* </ul>
*
* <p>Optional bindings with errors must be removed from the dependency graph before proceeding.
* This is to prevent them from incorrectly constraining the positions for keys that depend on them.
*
* <p>See {@link BindingResolver} for how this fits into the overall algorithm for resolution.
*/
public class UnresolvedBindingValidator {
 
  private final ErrorManager errorManager;
  private final EagerCycleFinder cycleFinder;
  private final TreeLogger logger;
 
  @Inject
  public UnresolvedBindingValidator(EagerCycleFinder cycleFinder, ErrorManager errorManager,
      @Assisted TreeLogger logger) {
    this.cycleFinder = cycleFinder;
    this.errorManager = errorManager;
    this.logger = logger;
  }
 
  /**
   * Returns an {@link InvalidKeys} object containing information about all the errors that we
   * discovered in required keys, and the set of all optional bindings that should be removed from
   * the graph in order to make it valid.
   */
  public InvalidKeys getInvalidKeys(DependencyExplorerOutput output) {
    Map<Key<?>, String> allInvalidKeys = getAllInvalidKeys(output);
    Set<Key<?>> optionalInvalidKeys = removeOptionalKeys(output, allInvalidKeys);
    optionalInvalidKeys = getKeysToRemove(output.getGraph(), optionalInvalidKeys);
    return new InvalidKeys(allInvalidKeys, optionalInvalidKeys);
  }
 
  /**
   * Returns true if the graph is valid (does not have any cycles or problems creating required
   * keys).  If there are any errors, they will be reported to the global {@link ErrorManager}.
   */
  public boolean validate(DependencyExplorerOutput output, InvalidKeys invalidKeys) {
    Collection<Map.Entry<Key<?>, String>> invalidRequiredKeys =
        invalidKeys.getInvalidRequiredKeys();
    for (Map.Entry<Key<?>, String> error : invalidRequiredKeys) {
      reportError(output, error.getKey(), error.getValue());
    }
   
    return !cycleFinder.findAndReportCycles(output.getGraph()) && invalidRequiredKeys.isEmpty();
  }
 
  /**
   * Prune all of the invalid optional keys from the graph.  After this method, all of the keys
   * remaining in the graph are resolvable.
   */
  public void pruneInvalidOptional(DependencyExplorerOutput output, InvalidKeys invalidKeys) {  
    DependencyGraph.GraphPruner prunedGraph = new DependencyGraph.GraphPruner(output.getGraph());
    for (Key<?> key : invalidKeys.getInvalidOptionalKeys()) {
      prunedGraph.remove(key);
      output.removeBinding(key);
    }
    output.setGraph(prunedGraph.update());
  }
 
  /**
   * Returns a map from keys that are invalid to errors explaining why each key is invalid.
   */
  private Map<Key<?>, String> getAllInvalidKeys(DependencyExplorerOutput output) {
    Map<Key<?>, String> invalidKeys = new LinkedHashMap<Key<?>, String>();
   
    // Look for errors in the nodes, and either report the error (if its required) or remove the
    // node (if its optional).
    for (Entry<Key<?>, String> error : output.getBindingErrors()) {
      invalidKeys.put(error.getKey(), "Unable to create or inherit binding: " + error.getValue());
    }
   
    GinjectorBindings origin = output.getGraph().getOrigin();
    for (Key<?> key : output.getImplicitlyBoundKeys()) {
      if (origin.isBoundLocallyInChild(key)) {

        GinjectorBindings child = origin.getChildWhichBindsLocally(key);
        Binding childBinding = child.getBinding(key);

        PrettyPrinter.log(logger, TreeLogger.DEBUG,
            "Marking the key %s as bound in the ginjector %s (implicitly), and in the child"
                + " %s (%s)", key, origin, child, childBinding.getContext());

        // TODO(schmitt): Determine path to binding in child ginjector (requires
        // different DependencyExplorerOutput).
        invalidKeys.put(key,
            PrettyPrinter.format("Already bound in child Ginjector %s. Consider exposing it?",
                child));
      }
    }
   
    return invalidKeys;
  }

  /**
   * Remove optional keys from the {@code invalidKeys} map, and return the the set of optional keys
   * that had problems.
   */
  private Set<Key<?>> removeOptionalKeys(
      DependencyExplorerOutput output, Map<Key<?>, String> invalidKeys) {
    RequiredKeySet requiredKeys = new RequiredKeySet(output.getGraph());
    Set<Key<?>> optionalKeys = new LinkedHashSet<Key<?>>();
    // We need to use a for loop instead of a foreach loop because we remove
    // entries as we go (see the call to iterator.remove() below).
    for (Iterator<Map.Entry<Key<?>, String>> iterator = invalidKeys.entrySet().iterator();
        iterator.hasNext();) {
      Map.Entry<Key<?>, String> entry = iterator.next();
      Key<?> key = entry.getKey();
      if (!requiredKeys.isRequired(key)) {
        PrettyPrinter.log(logger, TreeLogger.DEBUG,
            "Removing the optional key %s because it had errors: %s", key, entry.getValue());
        optionalKeys.add(key);
        iterator.remove();
      }
    }
    return optionalKeys;
  }
 
  /**
   * Given the set of optional keys that had problems, compute the set of all optional keys that
   * should be removed.  This may include additional keys, for instance if an optional key requires
   * an invalid key, than the optional key should also be removed.
   *
   * <p>This will only add optional keys to {@code toRemove}.  Recall that a key is required iff
   * there exists a path to it that passes through only required edges.  If key Y is optional, and
   * there is a required edge from X -> Y, then X must also be optional.  If X were required, by
   * our definition Y would also be required.
   */
  private Set<Key<?>> getKeysToRemove(DependencyGraph graph, Collection<Key<?>> discovered) {
    Set<Key<?>> toRemove = new LinkedHashSet<Key<?>>();
    while (!discovered.isEmpty()) {
      toRemove.addAll(discovered);
      discovered = getRequiredSourcesTargeting(graph, discovered);
      discovered.removeAll(toRemove);
    }
    return toRemove;
  }
 
  /**
   * Returns all of the source keys that have a required dependency on any key in the target set.
   */
  private Collection<Key<?>> getRequiredSourcesTargeting(
      DependencyGraph graph, Iterable<Key<?>> targets) {
    Collection<Key<?>> requiredSources = new LinkedHashSet<Key<?>>();
    for (Key<?> target : targets) {
      for (Dependency edge : graph.getDependenciesTargeting(target)) {
        if (!edge.isOptional()) {
          PrettyPrinter.log(logger, TreeLogger.DEBUG, "Removing the key %s because of %s",
              edge.getSource(), edge);
          requiredSources.add(edge.getSource());
        }
      }
    }
    return requiredSources;
  }
 
  private void reportError(DependencyExplorerOutput output, Key<?> key, String error) {
    // TODO(dburrows, bchambers): consider better approaches to pretty-printing keys.
    List<Dependency> path = new PathFinder()
        .onGraph(output.getGraph())
        .withOnlyRequiredEdges(true)
        .addRoots(Dependency.GINJECTOR)
        .addDestinations(key)
        .findShortestPath();

    errorManager.logError("Error injecting %s: %s%n  Path to required node:%n%s",
        key, error, path);
  }
 
  /**
   * Container for information about invalid keys.
   */
  public static class InvalidKeys {
    private final Map<Key<?>, String> invalidRequiredKeys;
    private final Collection<Key<?>> invalidOptionalKeys;
   
    private InvalidKeys(Map<Key<?>, String> invalidRequiredKeys, Set<Key<?>> invalidOptionalKeys) {
      this.invalidRequiredKeys = Collections.unmodifiableMap(invalidRequiredKeys);
      this.invalidOptionalKeys = Collections.unmodifiableCollection(invalidOptionalKeys);
    }
       
    public Set<Entry<Key<?>, String>> getInvalidRequiredKeys() {
      return invalidRequiredKeys.entrySet();
    }
   
    public Collection<Key<?>> getInvalidOptionalKeys() {
      return invalidOptionalKeys;
    }
  }

  public interface Factory {
    UnresolvedBindingValidator create(TreeLogger logger);
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.resolution.UnresolvedBindingValidator$Factory

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.