/*
* 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();
}
}
}