Package com.google.gwt.inject.rebind

Source Code of com.google.gwt.inject.rebind.GinjectorBindings

/*
* 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;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.inject.client.Ginjector;
import com.google.gwt.inject.client.assistedinject.FactoryModule;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.BindingIndex;
import com.google.gwt.inject.rebind.binding.Dependency;
import com.google.gwt.inject.rebind.binding.ExposedChildBinding;
import com.google.gwt.inject.rebind.binding.ParentBinding;
import com.google.gwt.inject.rebind.binding.RemoteServiceProxyBinding;
import com.google.gwt.inject.rebind.reflect.FieldLiteral;
import com.google.gwt.inject.rebind.reflect.MethodLiteral;
import com.google.gwt.inject.rebind.reflect.ReflectUtil;
import com.google.gwt.inject.rebind.resolution.BindingResolver;
import com.google.gwt.inject.rebind.util.GuiceUtil;
import com.google.gwt.inject.rebind.util.MemberCollector;
import com.google.gwt.inject.rebind.util.NameGenerator;
import com.google.gwt.inject.rebind.util.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionPoint;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Provider;

/**
* Stores information that describes the bindings present in a given injector,
* and the relationship to the other injectors in the hierarchy. This class is
* used in two stages:
*
* <ul>
* <li>During bindings processing {@link BindingsProcessor} this
* gathers up explicit bindings and unresolved dependencies. After all bindings
* have been gathered from the modules, {@link #resolveBindings} is called in
* the last stage of processing and it is finalized. After that point, no new
* unresolved bindings should be added.
* </li>
* <li>At this point, it is ready to be used by {@link GinjectorOutputter} for
* generating the Ginjector implementation. In this stage several additional
* methods are available for getting information about the code that has been
* generated to represent this ginjector.
* </li>
* </ul>
*
* <p>Each {@link GinjectorBindings} can have a parent ginjector, and any number of
* child ginjectors.
*/
public class GinjectorBindings implements BindingIndex {

  private final TreeLogger logger;

  /**
   * Generates names for code we produce to resolve injection requests.
   */
  private final NameGenerator nameGenerator;

  /**
   * Map from key to binding for all types we already have a binding for.  We use a LinkedHashMap
   * so that error reporting (and tests) will be deterministic.
   */
  private final Map<Key<?>, Binding> bindings = new LinkedHashMap<Key<?>, Binding>();
 
  /**
   * Set of all Dependency edges that this Ginjector is aware of.  This includes dependecies that
   * have been satisfied by bindings that are already available.  Some examples:
   * bind(Foo.class); adds the dependency GINJECTOR -> Foo
   * bind(Foo.class).to(FooImpl.class); adds the dependencies GINJECTOR -> Foo and Foo -> FooImpl
   *
   * <p>We use a LinkedHashSet so that error reporting (and tests) will be deterministic.
   */
  private final Set<Dependency> dependencies = new LinkedHashSet<Dependency>();

  /**
   * Map from key to scope for all types we have a binding for.
   */
  private final Map<Key<?>, GinScope> scopes = new LinkedHashMap<Key<?>, GinScope>();

  /**
   * Collection of keys for which the ginjector interface provides member inject
   * methods. If a regular binding is defined for the same key, no special
   * member inject handling is required - a member inject method will be created
   * as part of a regular binding.
   *
   * <p>We use a LinkedHashSet so that error reporting (and tests) will be deterministic.
   */
  private final Set<TypeLiteral<?>> memberInjectRequests = new LinkedHashSet<TypeLiteral<?>>();

  /**
   * Collection of all factory modules configured for this ginjector.
   *
   * <p>We use a LinkedHashSet so that error reporting (and tests) will be deterministic.
   */
  private final Set<FactoryModule<?>> factoryModules = new LinkedHashSet<FactoryModule<?>>();

  /**
   * All types for which static injection has been requested.
   *
   * <p>We use a LinkedHashSet so that error reporting (and tests) will be deterministic.
   */
  private final Set<Class<?>> staticInjectionRequests = new LinkedHashSet<Class<?>>();
 
  /**
   * The map of all keys that are bound locally in children of this ginjector to
   * the child binding it. "Locally" here means that they aren't inherited from
   * this ginjector or from one of its parents (speaking more pragmatically:
   * they aren't ParentBindings).  This is used when creating implicit
   * bindings. Specifically, we can't create an implicit binding here if any of
   * the children already bind it (even if its not exposed) because it would
   * lead to a double binding error.
   */
  private final Map<Key<?>, GinjectorBindings> boundLocallyInChildren =
      new LinkedHashMap<Key<?>, GinjectorBindings>();
 
  /**
   * Set of key's that *must* be bound here.  This corresponds to things that are explicitly bound
   * here.
   */
  private final Set<Key<?>> pinned = new LinkedHashSet<Key<?>>();

  /**
   * Collector that gathers all methods from an injector.
   */
  private final MemberCollector completeCollector;

  private final GuiceUtil guiceUtil;

  /**
   * Interface of the injector that this class is implementing.
   */
  private final TypeLiteral<? extends Ginjector> ginjectorInterface;

  private final ErrorManager errorManager;

  /**
   * The class (either Ginjector or Module) that this {@link GinjectorBindings}
   * is created for.
   */
  private Class<?> module;

  // Parent/Child information used for creating hierarchical injectors
  private GinjectorBindings parent = null;
  private final List<GinjectorBindings> children = new ArrayList<GinjectorBindings>();

  private final Provider<GinjectorBindings> ginjectorBindingsProvider;

  private final BindingResolver bindingResolver;

  /**
   * The {@link GinjectorBindings} are used in two "stages" -- during binding processing
   * (in {@link BindingsProcessor}) it is used to gather information, and during ginjector
   * generation where (in {@link GinjectorOutputter}) that information is read. To help
   * catch accidental mistakes this tracks which stage it is currently being used in.
   * TODO(bchambers): Split this class into two parts, and refactor the stages so that
   * they use the appropriate part.
   */
  private boolean finalized = false;

  @Inject
  public GinjectorBindings(NameGenerator nameGenerator,
      TreeLogger logger,
      GuiceUtil guiceUtil,
      @GinjectorInterfaceType Class<? extends Ginjector> ginjectorInterface,
      Provider<GinjectorBindings> ginjectorBindingsProvider,
      MemberCollector collector,
      ErrorManager errorManager,
      BindingResolver bindingResolver) {
    this.nameGenerator = nameGenerator;
    this.logger = logger;
    this.guiceUtil = guiceUtil;
    this.bindingResolver = bindingResolver;
    this.ginjectorInterface = TypeLiteral.get(ginjectorInterface);
    this.ginjectorBindingsProvider = ginjectorBindingsProvider;
    this.errorManager = errorManager;

    completeCollector = collector;
    completeCollector.setMethodFilter(MemberCollector.ALL_METHOD_FILTER);
  }

  void assertFinalized() {
    Preconditions.checkState(finalized,
        "Can only use this method after finalizing the ginjector bindings!");
  }

  void assertNotFinalized() {
    Preconditions.checkState(!finalized,
        "Can only use this method before finalizing the ginjector bindings!");
  }

  /**
   * Create a new {@link GinjectorBindings} that collects bindings for an injector that is the
   * child of this {@link GinjectorBindings}.
   *
   * @param module the module the ginjector is being created for
   * @return the child {@link GinjectorBindings}
   */
  public GinjectorBindings createChildGinjectorBindings(Class<?> module) {
    assertNotFinalized();
    GinjectorBindings child = ginjectorBindingsProvider.get();
    child.setParent(this);
    child.setModule(module);
    children.add(child);
    return child;
  }

  public void resolveBindings() throws UnableToCompleteException {
    assertNotFinalized();
   
    bindingResolver.resolveBindings(this);
    errorManager.checkForError();

    // Mark this collection as finalized, so that no new bindings or unresolved
    // dependencies
    // can be added.
    finalized = true;
  }

  public Iterable<Dependency> getDependencies() {
    assertNotFinalized();
    return Collections.unmodifiableCollection(dependencies);
  }

  public Iterable<Key<?>> getBoundKeys() {
    return Collections.unmodifiableCollection(bindings.keySet());
  }

  public Iterable<Map.Entry<Key<?>, Binding>> getBindings() {
    return Collections.unmodifiableCollection(bindings.entrySet());
  }

  public TypeLiteral<?> getGinjectorInterface() {
    return ginjectorInterface;
  }

  public Collection<Class<?>> getStaticInjectionRequests() {
    return Collections.unmodifiableCollection(staticInjectionRequests);
  }

  public Iterable<TypeLiteral<?>> getMemberInjectRequests() {
    return Collections.unmodifiableCollection(memberInjectRequests);
  }

  /**
   * Returns {@code true} if this bindings object contains at least one eager
   * singleton binding.
   */
  private boolean hasEagerSingletonBinding() {
    for (Key<?> key : bindings.keySet()) {
      GinScope scope = determineScope(key);
      if (GinScope.EAGER_SINGLETON.equals(scope)) {
        return true;
      }
    }

    return false;
  }

  /**
   * Returns {@code true} if any binding in this injector or in one of its
   * descendants is an eager singleton binding.
   *
   * <p>Note: this method is Omega(n) in the height of the injector tree, and
   * invoking it on every entry in the injector tree is O(n^2).  The latter cost
   * could be reduced to O(n) by caching the return value.
   */
  public boolean hasEagerSingletonBindingInSubtree() {
    if (hasEagerSingletonBinding()) {
      return true;
    }

    for (GinjectorBindings child : getChildren()) {
      if (child.hasEagerSingletonBindingInSubtree()) {
        return true;
      }
    }

    return false;
  }

  /**
   * Returns {@code true} if this injector or any of its children has a static
   * injection request.
   *
   * <p>Note: this method is Omega(n) in the height of the injector tree, and
   * invoking it on every entry in the injector tree is O(n^2).  The latter cost
   * could be reduced to O(n) by caching the return value.
   */
  public boolean hasStaticInjectionRequestInSubtree() {
    if (!staticInjectionRequests.isEmpty()) {
      return true;
    }

    for (GinjectorBindings child : getChildren()) {
      if (child.hasStaticInjectionRequestInSubtree()) {
        return true;
      }
    }

    return false;
  }

  void putScope(Key<?> key, GinScope scope) {
    scopes.put(key, scope);
  }

  public GinjectorBindings getParent() {
    return parent;
  }

  public void setParent(GinjectorBindings parent) {
    assertNotFinalized();
    this.parent = parent;
  }

  public Class<?> getModule() {
    return module;
  }
 
  public String getModuleName() {
    return getModule().getSimpleName();
  }

  public void setModule(Class<?> module) {
    this.module = module;
  }

  public Iterable<GinjectorBindings> getChildren() {
    return Collections.unmodifiableCollection(children);
  }
 
  public Iterable<FactoryModule<?>> getFactoryModules() {
    return Collections.unmodifiableCollection(factoryModules);
  }

  public NameGenerator getNameGenerator() {
    assertFinalized();
    return nameGenerator;
  }

  public GinScope determineScope(Key<?> key) {
    assertFinalized();
    GinScope scope = scopes.get(key);
    if (scope == null) {
      Class<?> raw = key.getTypeLiteral().getRawType();
      Binding binding = bindings.get(key);
      if (binding != null
          && (binding instanceof ExposedChildBinding
              || binding instanceof ParentBinding)) {
        // If this is just a "copy" of a binding higher/lower in the injector
        // tree, we prefer to treat the binding like it's unscoped, and refer to
        // the "real" binding every time we need the value.
        scope = GinScope.NO_SCOPE;
      } else if (raw.getAnnotation(Singleton.class) != null
          || raw.getAnnotation(javax.inject.Singleton.class) != null) {
        // Look for scope annotation as a fallback
        scope = GinScope.SINGLETON;
      } else if (RemoteServiceProxyBinding.isRemoteServiceProxy(key.getTypeLiteral())) {
        // Special case for remote services
        scope = GinScope.SINGLETON;
      } else {
        scope = GinScope.NO_SCOPE;
      }
    }

    logger.log(TreeLogger.TRACE, "scope for " + key + ": " + scope);
    return scope;
  }

  public boolean isBound(Key<?> key) {
    return bindings.containsKey(key);
  }

  public Binding getBinding(Key<?> key) {
    return bindings.get(key);
  }

  public void addDependency(Dependency dependency) {
    assertNotFinalized();
    dependencies.add(dependency);
  }
 
  public void addDependencies(Collection<Dependency> dependencies) {
    assertNotFinalized();
    this.dependencies.addAll(dependencies);
  }

  void addUnresolvedEntriesForInjectorInterface() {
    assertNotFinalized();
    for (MethodLiteral<?, Method> method : completeCollector.getMethods(ginjectorInterface)) {
      nameGenerator.markAsUsed(method.getName());
      Key<?> key = guiceUtil.getKey(method);
      logger.log(TreeLogger.TRACE, "Add unresolved key from injector interface: " + key);

      // Member inject types do not need to be gin-creatable themselves but we
      // need to provide all dependencies.
      if (guiceUtil.isMemberInject(method)) {
        memberInjectRequests.add(key.getTypeLiteral());
        addDependencies(guiceUtil.getMemberInjectionDependencies(
            Dependency.GINJECTOR, key.getTypeLiteral()));
      } else {
        addDependency(new Dependency(Dependency.GINJECTOR, key, method.toString()));
      }
    }
  }
 
  public void addBinding(Key<?> key, Binding binding) {
    assertNotFinalized();
    if (bindings.containsKey(key)) {
      errorManager.logDoubleBind(key, bindings.get(key), this, binding, this);
      return;
    }

    if (!isClassAccessibleFromPackage(key.getTypeLiteral())) {
      errorManager.logError("Can not inject an instance of an inaccessible class: %s", key);
      return;
    }

    bindings.put(key, binding);
    if (parent != null && !(binding instanceof ParentBinding)) {
      parent.registerLocalChildBinding(key, this);
    }

    logger.log(TreeLogger.TRACE, "bound " + key + " to " + binding);
    dependencies.addAll(binding.getDependencies());
    memberInjectRequests.addAll(binding.getMemberInjectRequests());
  }
 
  public void addPin(Key<?> key) {
    pinned.add(key);
  }
 
  public boolean isPinned(Key<?> key) {
    return pinned.contains(key);
  }
 
  /**
   * Register the key in the "boundLocallyInChildren" set for this injector, and
   * recursively register it with all of the ancestors.  The caller is
   * responsible for ensuring that the binding being registered is actually
   * local (i.e., not a ParentBinding).
   */
  private void registerLocalChildBinding(Key<?> key, GinjectorBindings binding) {
    boundLocallyInChildren.put(key, binding);
    if (parent != null) {
      parent.registerLocalChildBinding(key, binding);
    }
  }
  public boolean isBoundLocallyInChild(Key<?> key) {
    return boundLocallyInChildren.containsKey(key);
  }

  /**
   * Returns the child injector which binds the given key. If no child binds the key, returns
   * {@code null}.
   */
  public GinjectorBindings getChildWhichBindsLocally(Key<?> key) {
    return boundLocallyInChildren.get(key);
  }

  private boolean isClassAccessibleFromPackage(TypeLiteral<?> type) {
    return !ReflectUtil.isPrivate(type);
  }

  void addStaticInjectionRequest(Class<?> type, Object source) {
    assertNotFinalized();
    staticInjectionRequests.add(type);

    // Calculate required bindings and add to dependencies
    for (InjectionPoint injectionPoint : InjectionPoint.forStaticMethodsAndFields(type)) {
      Member member = injectionPoint.getMember();
      if (member instanceof Method) {
        addDependencies(guiceUtil.getDependencies(Dependency.GINJECTOR,
            MethodLiteral.get((Method) member, TypeLiteral.get(member.getDeclaringClass()))));
      } else if (member instanceof Field) {
        FieldLiteral<?> field =
            FieldLiteral.get((Field) member, TypeLiteral.get(member.getDeclaringClass()));
        Key<?> key = guiceUtil.getKey(field);
        addDependency(new Dependency(
            Dependency.GINJECTOR, key, guiceUtil.isOptional(field), false,
            source.toString()));
      }
    }
  }

  public void addFactoryModule(FactoryModule<?> install) {
    factoryModules.add(install);
    // Prevent the factory interface from floating away or being
    // implicitly instantiated.
    addPin(install.getFactoryType());
  }

  @Override
  public String toString() {
    if (parent == null) {
      return ginjectorInterface.toString();
    } else {
      return module.getCanonicalName();
    }
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.GinjectorBindings

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.