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

Source Code of com.google.gwt.inject.rebind.resolution.DependencyExplorer$DependencyExplorerOutput

/*
* 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.core.ext.TreeLogger.Type;
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.ExposedChildBinding;
import com.google.gwt.inject.rebind.resolution.ImplicitBindingCreator.BindingCreationException;
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.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
* Explores the unresolved dependencies for a given Ginjector and builds the {@link DependencyGraph}
* representing all of the implicit bindings that need to be created to satisfy the dependencies.
*
* <p>See {@link BindingResolver} for how this fits into the overall algorithm for resolution.
*/
public class DependencyExplorer {
 
  private final TreeLogger logger;
 
  private final Set<Key<?>> visited = new LinkedHashSet<Key<?>>();
 
  private final ImplicitBindingCreator bindingCreator;
 
  @Inject
  public DependencyExplorer(ImplicitBindingCreator.Factory bindingCreatorFactory,
      @Assisted TreeLogger logger) {
    this.bindingCreator = bindingCreatorFactory.create(logger);
    this.logger = logger;
  }
 
  /**
   * Explore the unresolved dependencies in the origin Ginjector, and create the corresponding
   * dependency graph.  Also gathers information about key in the dependency graph, such as which
   * Ginjector it is already available on, or what implicit binding was created for it.
   *
   * @param origin the ginjector to build a dependency graph for
   */
  public DependencyExplorerOutput explore(GinjectorBindings origin) {
    DependencyExplorerOutput output = new DependencyExplorerOutput();
    DependencyGraph.Builder builder = new DependencyGraph.Builder(origin);
   
    for (Dependency edge : origin.getDependencies()) {
      Preconditions.checkState(
          Dependency.GINJECTOR.equals(edge.getSource()) || origin.isBound(edge.getSource()),
          "Expected non-null source %s to be bound in origin!", edge.getSource());
      builder.addEdge(edge);
      if (!edge.getSource().equals(Dependency.GINJECTOR) && visited.add(edge.getSource())) {
        // Need to register where we can find the "source".  Note that this will be always be
        // available somewhere, because it's already available at the origin (that's how we found
        // this dependency).
        PrettyPrinter.log(logger, TreeLogger.DEBUG,
              "Registering %s as available at %s because of the dependency %s", edge.getSource(),
              origin, edge);
        output.preExistingBindings.put(edge.getSource(),
            locateHighestAccessibleSource(edge.getSource(), origin));
      }

      PrettyPrinter.log(logger, TreeLogger.DEBUG,
          "Exploring from %s in %s because of the dependency %s", edge.getTarget(), origin, edge);
      // Visit the target of the dependency to find additional bindings
      visit(edge.getTarget(), builder, output, origin);
    }
   
    output.setGraph(builder.build());
    return output;
  }
  private void visit(Key<?> key, DependencyGraph.Builder builder,
      DependencyExplorerOutput output, GinjectorBindings origin) {
    if (visited.add(key)) {
      GinjectorBindings accessibleSource = locateHighestAccessibleSource(key, origin);
      if (accessibleSource != null) {
        PrettyPrinter.log(logger, TreeLogger.DEBUG, "Using binding of %s in %s.", key,
            accessibleSource);
        output.preExistingBindings.put(key, accessibleSource);
      } else {
        try {
          Binding binding = bindingCreator.create(key);
          PrettyPrinter.log(logger, TreeLogger.DEBUG, "Implicitly bound %s in %s using %s.", key,
              origin, binding);
         
          for (Dependency edge : binding.getDependencies()) {
            PrettyPrinter.log(logger, TreeLogger.DEBUG, "Following %s", edge);
            builder.addEdge(edge);
            visit(edge.getTarget(), builder, output, origin);
          }
         
          // Do this *after* visiting all dependencies so that that the ordering is post-order
          output.implicitBindings.put(key, binding);
        } catch (BindingCreationException e) {
          PrettyPrinter.log(logger, TreeLogger.DEBUG, "Implicit binding failed for %s: %s", key,
              e.getMessage());
          output.bindingErrors.put(key, e.getMessage());
        } catch (RuntimeException e) {
          logger.log(Type.ERROR, "Exception while visiting " + key);
          throw e;
        }
      }
    }
  }
 
  /**
   * Find the highest binding in the Ginjector tree that could be used to supply the given key.
   *
   * <p>This takes care not to use a higher parent binding if the parent only has the binding
   * because its exposed from this Ginjector.  This leads to problems because the resolution
   * algorithm won't actually create the binding here if it can just use a parents binding.
   *
   * @param key The binding to search for
   * @return the highest ginjector that contains the key or {@code null} if none contain it
   */
  private GinjectorBindings locateHighestAccessibleSource(Key<?> key, GinjectorBindings origin) {
    // If we don't already have a binding, and the key is "pinned", it means that this injector
    // is supposed to contain a binding, but it needs to have one created implicitly.  We return
    // null so that we attempt to create the implicit binding.
    if (!origin.isBound(key) && origin.isPinned(key)) {
      return null;
    }

    GinjectorBindings source = null;
    for (GinjectorBindings iter = origin; iter != null; iter = iter.getParent()) {
      // If the key is already explicitly bound, or has a pin indicating that it
      // will be explicitly bound once it gets visited (we visit children before
      // parents) then we can access the binding from the given location.
      if (iter.isBound(key) || iter.isPinned(key)) {
        source = iter;
      }
    }
    return source;
  }
 
  /**
   * Class that packages up all the output of exploring the unresolved dependencies for a Ginjector.
   * This contains the {@link DependencyGraph} itself, as well as additional information about the
   * nodes.
   */
  public static class DependencyExplorerOutput {
   
    private final Map<Key<?>, GinjectorBindings> preExistingBindings =
        new LinkedHashMap<Key<?>, GinjectorBindings>();
    private final LinkedHashMap<Key<?>, Binding> implicitBindings =
        new LinkedHashMap<Key<?>, Binding>();
    private final LinkedHashMap<Key<?>, String> bindingErrors = new LinkedHashMap<Key<?>, String>();
    private DependencyGraph graph;
   
    private DependencyExplorerOutput() {}
   
    void setGraph(DependencyGraph graph) {
      this.graph = graph;
    }
   
    public int size() {
      return graph.size();
    }
  
    /**
     * Returns a map from each {@code Key<?>} that was already available in the injector hierarchy
     * to the Ginjector on which it was found.
     */
    public Map<Key<?>, GinjectorBindings> getPreExistingLocations() {
      return Collections.unmodifiableMap(preExistingBindings);
    }
   
    /**
     * Return the {@code Key<?>}s that weren't already available and for which we successfully
     * created implicit bindings.
     */
    public Collection<Key<?>> getImplicitlyBoundKeys() {
      return Collections.unmodifiableCollection(implicitBindings.keySet());
    }
   
    /**
     * Returns pairs containing the {@code Key<?>}s that were unavailable from the injector
     * hierarchy but that we were unable to create implicit bindings for and an error message
     * describing the problem we encountered while creating the implicit binding.
     */
    public Collection<Map.Entry<Key<?>, String>> getBindingErrors() {
      return Collections.unmodifiableCollection(bindingErrors.entrySet());
    }
   
    /**
     * Returns map entries containing the {@code Key<?>}s that weren't already available and the
     * {@link Binding} we created (implicitly) for it.  If there was an error creating the implicit
     * binding, the key will not be found here.  Look in {@link #getBindingErrors()} instead.
     */
    public Collection<Map.Entry<Key<?>, Binding>> getImplicitBindings() {
      return Collections.unmodifiableCollection(implicitBindings.entrySet());
    }
   
    /**
     * Removes an implicit binding from the information being tracked.
     */
    public void removeBinding(Key<?> key) {
      implicitBindings.remove(key);
    }
   
    /**
     * Returns the {@link DependencyGraph} containing information about nodes found from the origin.
     */
    public DependencyGraph getGraph() {
      return graph;
    }
  }

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

Related Classes of com.google.gwt.inject.rebind.resolution.DependencyExplorer$DependencyExplorerOutput

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.