| /* |
| * Copyright (C) 2007 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.inject; |
| |
| import com.google.inject.BinderImpl.CreationListener; |
| import com.google.inject.BinderImpl.MembersInjector; |
| import com.google.inject.binder.AnnotatedBindingBuilder; |
| import com.google.inject.binder.ScopedBindingBuilder; |
| import com.google.inject.internal.Annotations; |
| import com.google.inject.internal.Objects; |
| import com.google.inject.internal.StackTraceElements; |
| import com.google.inject.internal.ToStringBuilder; |
| import com.google.inject.spi.BindingVisitor; |
| import com.google.inject.spi.Dependency; |
| import com.google.inject.spi.InstanceBinding; |
| import com.google.inject.spi.ProviderInstanceBinding; |
| import com.google.inject.util.Providers; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Type; |
| import java.util.logging.Logger; |
| import java.util.Collection; |
| |
| /** |
| * Binds a {@link com.google.inject.Key} to an implementation in a given scope. |
| */ |
| class BindingBuilderImpl<T> implements AnnotatedBindingBuilder<T> { |
| |
| private static final Logger logger |
| = Logger.getLogger(BindingBuilderImpl.class.getName()); |
| |
| final Object source; |
| Key<T> key; |
| InternalFactory<? extends T> factory; |
| Scope scope; |
| boolean preload = false; |
| private BinderImpl binder; |
| |
| // These fields keep track of the raw implementation for later use in the |
| // Binding API. |
| T instance; |
| Key<? extends T> targetKey; |
| Provider<? extends T> providerInstance; |
| Key<? extends Provider<? extends T>> providerKey; |
| |
| BindingBuilderImpl(BinderImpl binder, Key<T> key, Object source) { |
| this.binder = binder; |
| this.key = Objects.nonNull(key, "key"); |
| this.source = source; |
| } |
| |
| Object getSource() { |
| return source; |
| } |
| |
| Key<T> getKey() { |
| return key; |
| } |
| |
| public BindingBuilderImpl<T> annotatedWith( |
| Class<? extends Annotation> annotationType) { |
| if (this.key.hasAnnotationType()) { |
| binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED); |
| } else { |
| boolean retainedAtRuntime = |
| Annotations.isRetainedAtRuntime(annotationType); |
| boolean bindingAnnotation = Key.isBindingAnnotation(annotationType); |
| |
| if (!retainedAtRuntime) { |
| binder.addError(StackTraceElements.forType(annotationType), |
| ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source()); |
| } |
| |
| if (!bindingAnnotation) { |
| binder.addError(StackTraceElements.forType(annotationType), |
| ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source()); |
| } |
| |
| if (retainedAtRuntime && bindingAnnotation) { |
| this.key = Key.get(this.key.getTypeLiteral(), annotationType); |
| } |
| } |
| return this; |
| } |
| |
| public BindingBuilderImpl<T> annotatedWith(Annotation annotation) { |
| if (this.key.hasAnnotationType()) { |
| binder.addError(source, ErrorMessages.ANNOTATION_ALREADY_SPECIFIED); |
| } else { |
| Class<? extends Annotation> annotationType = annotation.annotationType(); |
| |
| boolean retainedAtRuntime = |
| Annotations.isRetainedAtRuntime(annotationType); |
| boolean bindingAnnotation = Key.isBindingAnnotation(annotationType); |
| |
| if (!retainedAtRuntime) { |
| binder.addError(StackTraceElements.forType(annotationType), |
| ErrorMessages.MISSING_RUNTIME_RETENTION, binder.source()); |
| } |
| |
| if (!bindingAnnotation) { |
| binder.addError(StackTraceElements.forType(annotationType), |
| ErrorMessages.MISSING_BINDING_ANNOTATION, binder.source()); |
| } |
| |
| if (retainedAtRuntime && bindingAnnotation) { |
| this.key = Key.get(this.key.getTypeLiteral(), annotation); |
| } |
| } |
| return this; |
| } |
| |
| public ScopedBindingBuilder to(Class<? extends T> implementation) { |
| return to(TypeLiteral.get(implementation)); |
| } |
| |
| public ScopedBindingBuilder to(TypeLiteral<? extends T> implementation) { |
| return to(Key.get(implementation)); |
| } |
| |
| public ScopedBindingBuilder to(Key<? extends T> targetKey) { |
| ensureImplementationIsNotSet(); |
| |
| if (key.equals(targetKey)) { |
| binder.addError(source, ErrorMessages.RECURSIVE_BINDING); |
| } |
| |
| final FactoryProxy<? extends T> factoryProxy = |
| new FactoryProxy<T>(key, targetKey, source); |
| this.factory = factoryProxy; |
| binder.creationListeners.add(factoryProxy); |
| this.targetKey = targetKey; |
| return this; |
| } |
| |
| public void toInstance(T instance) { |
| ensureImplementationIsNotSet(); |
| this.instance = instance; |
| this.factory = new ConstantFactory<T>(instance); |
| registerInstanceForInjection(instance); |
| |
| // TODO: I don't think this can happen anymore. |
| if (this.scope != null) { |
| binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE); |
| } |
| } |
| |
| public ScopedBindingBuilder toProvider(Provider<? extends T> provider) { |
| ensureImplementationIsNotSet(); |
| this.factory = new InternalFactoryToProviderAdapter<T>(provider, source); |
| this.providerInstance = provider; |
| registerInstanceForInjection(provider); |
| return this; |
| } |
| |
| public BindingBuilderImpl<T> toInternalFactory(InternalFactory<T> internalFactory) { |
| ensureImplementationIsNotSet(); |
| this.factory = internalFactory; |
| this.providerInstance = new Provider<T>() { |
| public T get() { |
| throw new UnsupportedOperationException("?"); |
| } |
| }; |
| return this; |
| } |
| |
| public BindingBuilderImpl<T> toProvider( |
| Class<? extends Provider<? extends T>> providerType) { |
| return toProvider(Key.get(providerType)); |
| } |
| |
| public BindingBuilderImpl<T> toProvider( |
| Key<? extends Provider<? extends T>> providerKey) { |
| ensureImplementationIsNotSet(); |
| |
| final BoundProviderFactory<T> boundProviderFactory = |
| new BoundProviderFactory<T>(providerKey, source); |
| binder.creationListeners.add(boundProviderFactory); |
| this.factory = boundProviderFactory; |
| this.providerKey = providerKey; |
| |
| return this; |
| } |
| |
| /** |
| * Adds an error message if the implementation has already been bound. |
| */ |
| private void ensureImplementationIsNotSet() { |
| if (factory != null) { |
| binder.addError(source, ErrorMessages.IMPLEMENTATION_ALREADY_SET); |
| } |
| } |
| |
| public void in(Class<? extends Annotation> scopeAnnotation) { |
| // this method not test-covered |
| ensureScopeNotSet(); |
| |
| // We could defer this lookup to when we create the Injector, but this |
| // is fine for now. |
| this.scope = binder.scopes.get( |
| Objects.nonNull(scopeAnnotation, "scope annotation")); |
| if (this.scope == null) { |
| binder.addError(source, ErrorMessages.SCOPE_NOT_FOUND, |
| "@" + scopeAnnotation.getSimpleName()); |
| } |
| } |
| |
| public void in(Scope scope) { |
| ensureScopeNotSet(); |
| this.scope = Objects.nonNull(scope, "scope"); |
| } |
| |
| private void ensureScopeNotSet() { |
| // Scoping isn't allowed when we have only one instance. |
| if (this.instance != null) { |
| binder.addError(source, ErrorMessages.SINGLE_INSTANCE_AND_SCOPE); |
| return; |
| } |
| |
| if (this.scope != null) { |
| binder.addError(source, ErrorMessages.SCOPE_ALREADY_SET); |
| } |
| } |
| |
| public void asEagerSingleton() { |
| in(Scopes.SINGLETON); |
| this.preload = true; |
| } |
| |
| boolean shouldPreload() { |
| return preload; |
| } |
| |
| BindingImpl<T> build(InjectorImpl injector) { |
| if (this.factory != null) { |
| Scope scope = this.scope == null ? Scopes.NO_SCOPE : this.scope; |
| |
| InternalFactory<? extends T> scopedFactory |
| = Scopes.scope(this.key, injector, this.factory, scope); |
| |
| // Instance binding. |
| if (instance != null) { |
| return new InstanceBindingImpl<T>( |
| injector, key, source, scopedFactory, instance); |
| } |
| |
| // Linked binding. |
| if (this.targetKey != null) { |
| return new LinkedBindingImpl<T>( |
| injector, key, source, scopedFactory, scope, targetKey); |
| } |
| |
| // Provider instance binding. |
| if (this.providerInstance != null) { |
| return new ProviderInstanceBindingImpl<T>( |
| injector, key, source, scopedFactory, scope, providerInstance); |
| } |
| |
| // Provider binding. |
| if (this.providerKey != null) { |
| return new LinkedProviderBindingImpl<T>( |
| injector, key, source, scopedFactory, scope, providerKey); |
| } |
| |
| throw new AssertionError(); |
| } else { |
| // If we're here, the type we bound to is also the implementation. |
| // Example: bind(FooImpl.class).in(Scopes.SINGLETON); |
| |
| Type type = key.getTypeLiteral().getType(); |
| |
| // Error: Missing implementation. |
| // Example: bind(Date.class).annotatedWith(Red.class); |
| // We can't assume abstract types aren't injectable. They may have an |
| // @ImplementedBy annotation or something. |
| if (key.hasAnnotationType() || !(type instanceof Class<?>)) { |
| injector.errorHandler.handle(source, |
| ErrorMessages.MISSING_IMPLEMENTATION); |
| return invalidBinding(injector); |
| } |
| |
| // This cast is safe after the preceeding check. |
| @SuppressWarnings("unchecked") |
| Class<T> clazz = (Class<T>) type; |
| |
| BindingImpl<T> binding = injector.createBindingFromType( |
| clazz, scope, source); |
| // TODO: Should we clean up the binding left behind in jitBindings? |
| |
| if (binding == null) { |
| injector.errorHandler.handle(source, |
| ErrorMessages.CANNOT_INJECT_ABSTRACT_TYPE, clazz); |
| return invalidBinding(injector); |
| } |
| |
| return binding; |
| } |
| } |
| |
| InvalidBindingImpl<T> invalidBinding(InjectorImpl injector) { |
| return new InvalidBindingImpl<T>(injector, key, source); |
| } |
| |
| private static class InstanceBindingImpl<T> extends BindingImpl<T> |
| implements InstanceBinding<T> { |
| |
| final T instance; |
| final Provider<T> provider; |
| |
| InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source, |
| InternalFactory<? extends T> internalFactory, T instance) { |
| super(injector, key, source, internalFactory, Scopes.NO_SCOPE); |
| this.instance = instance; |
| this.provider = Providers.of(instance); |
| } |
| |
| @Override |
| public Provider<T> getProvider() { |
| return this.provider; |
| } |
| |
| public void accept(BindingVisitor<? super T> bindingVisitor) { |
| bindingVisitor.visit(this); |
| } |
| |
| public T getInstance() { |
| return this.instance; |
| } |
| |
| public Collection<Dependency<?>> getDependencies() { |
| return injector.getFieldAndMethodDependenciesFor(instance.getClass()); |
| } |
| |
| @Override |
| public String toString() { |
| return new ToStringBuilder(InstanceBinding.class) |
| .add("key", key) |
| .add("instance", instance) |
| .add("source", source) |
| .toString(); |
| } |
| } |
| |
| private static class ProviderInstanceBindingImpl<T> extends BindingImpl<T> |
| implements ProviderInstanceBinding<T> { |
| |
| final Provider<? extends T> providerInstance; |
| |
| ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key, |
| Object source, |
| InternalFactory<? extends T> internalFactory, Scope scope, |
| Provider<? extends T> providerInstance) { |
| super(injector, key, source, internalFactory, scope); |
| this.providerInstance = providerInstance; |
| } |
| |
| public void accept(BindingVisitor<? super T> bindingVisitor) { |
| bindingVisitor.visit(this); |
| } |
| |
| public Provider<? extends T> getProviderInstance() { |
| return this.providerInstance; |
| } |
| |
| public Collection<Dependency<?>> getDependencies() { |
| return injector.getFieldAndMethodDependenciesFor( |
| providerInstance.getClass()); |
| } |
| |
| @Override |
| public String toString() { |
| return new ToStringBuilder(ProviderInstanceBinding.class) |
| .add("key", key) |
| .add("provider", providerInstance) |
| .add("scope", scope) |
| .add("source", source) |
| .toString(); |
| } |
| } |
| |
| void registerInstanceForInjection(final Object o) { |
| binder.membersInjectors.add(new MembersInjector(o)); |
| } |
| |
| /** |
| * A placeholder which enables us to swap in the real factory once the |
| * container is created. |
| */ |
| private static class FactoryProxy<T> implements InternalFactory<T>, |
| CreationListener { |
| |
| private final Key<T> key; |
| private final Key<? extends T> targetKey; |
| private final Object source; |
| |
| InternalFactory<? extends T> targetFactory; |
| |
| FactoryProxy(Key<T> key, Key<? extends T> targetKey, Object source) { |
| this.key = key; |
| this.targetKey = targetKey; |
| this.source = source; |
| } |
| |
| public void notify(final InjectorImpl injector) { |
| injector.withDefaultSource(source, new Runnable() { |
| public void run() { |
| targetFactory = injector.getInternalFactory(targetKey); |
| } |
| }); |
| } |
| |
| public T get(InternalContext context, InjectionPoint<?> injectionPoint) { |
| return targetFactory.get(context, injectionPoint); |
| } |
| |
| public String toString() { |
| return new ToStringBuilder(FactoryProxy.class) |
| .add("key", key) |
| .add("provider", targetFactory) |
| .toString(); |
| } |
| } |
| } |