| /** |
| * Copyright (C) 2008 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.spi; |
| |
| import com.google.inject.Binder; |
| import com.google.inject.Binding; |
| import com.google.inject.Key; |
| import com.google.inject.MembersInjector; |
| import com.google.inject.Module; |
| import com.google.inject.PrivateBinder; |
| import com.google.inject.Provider; |
| import com.google.inject.Scope; |
| import com.google.inject.binder.ScopedBindingBuilder; |
| import com.google.inject.internal.Maps; |
| import static com.google.inject.internal.Preconditions.checkArgument; |
| import static com.google.inject.internal.Preconditions.checkNotNull; |
| import java.lang.annotation.Annotation; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Creates a Module from a collection of component elements. |
| * |
| * @author jessewilson@google.com (Jesse Wilson) |
| * @since 2.0 |
| */ |
| public class ModuleWriter { |
| |
| private final Map<PrivateElements, PrivateBinder> environmentToBinder = Maps.newHashMap(); |
| |
| /** |
| * Returns a module that executes the specified elements using this executing visitor. |
| */ |
| public final Module create(final Iterable<? extends Element> elements) { |
| return new Module() { |
| public void configure(Binder binder) { |
| apply(binder, elements); |
| } |
| }; |
| } |
| |
| /** |
| * Replays {@code elements} against {@code binder}. |
| */ |
| public final void apply(final Binder binder, Iterable<? extends Element> elements) { |
| checkNotNull(binder, "binder"); |
| checkNotNull(elements, "elements"); |
| |
| ElementVisitor<Void> visitor = new ElementVisitor<Void>() { |
| |
| public Void visit(Message message) { |
| writeMessage(binder, message); |
| return null; |
| } |
| |
| /*if[AOP]*/ |
| public Void visit(InterceptorBinding element) { |
| writeBindInterceptor(binder, element); |
| return null; |
| } |
| /*end[AOP]*/ |
| |
| public Void visit(ScopeBinding element) { |
| writeBindScope(binder, element); |
| return null; |
| } |
| |
| public Void visit(InjectionRequest element) { |
| writeRequestInjection(binder, element); |
| return null; |
| } |
| |
| public Void visit(StaticInjectionRequest element) { |
| writeRequestStaticInjection(binder, element); |
| return null; |
| } |
| |
| public Void visit(TypeConverterBinding element) { |
| writeConvertToTypes(binder, element); |
| return null; |
| } |
| |
| public <T> Void visit(Binding<T> element) { |
| writeBind(binder, element); |
| return null; |
| } |
| |
| public <T> Void visit(ProviderLookup<T> element) { |
| writeGetProvider(binder, element); |
| return null; |
| } |
| |
| public <T> Void visit(MembersInjectorLookup<T> element) { |
| writeGetMembersInjector(binder, element); |
| return null; |
| } |
| |
| public Void visit(InjectableTypeListenerBinding binding) { |
| throw new UnsupportedOperationException("TODO"); |
| } |
| |
| public Void visit(PrivateElements privateElements) { |
| writePrivateElements(binder, privateElements); |
| return null; |
| } |
| }; |
| |
| for (Element element : elements) { |
| element.acceptVisitor(visitor); |
| } |
| } |
| |
| protected void writeMessage(Binder binder, Message element) { |
| binder.addError(element); |
| } |
| |
| /*if[AOP]*/ |
| protected void writeBindInterceptor(Binder binder, InterceptorBinding element) { |
| List<org.aopalliance.intercept.MethodInterceptor> interceptors = element.getInterceptors(); |
| binder.withSource(element.getSource()).bindInterceptor( |
| element.getClassMatcher(), element.getMethodMatcher(), |
| interceptors.toArray(new org.aopalliance.intercept.MethodInterceptor[interceptors.size()])); |
| } |
| /*end[AOP]*/ |
| |
| protected void writeBindScope(Binder binder, ScopeBinding element) { |
| binder.withSource(element.getSource()).bindScope( |
| element.getAnnotationType(), element.getScope()); |
| } |
| |
| protected void writeRequestInjection(Binder binder, InjectionRequest command) { |
| binder.withSource(command.getSource()).requestInjection(command.getInstance()); |
| } |
| |
| protected void writeRequestStaticInjection(Binder binder, StaticInjectionRequest element) { |
| Class<?> type = element.getType(); |
| binder.withSource(element.getSource()).requestStaticInjection(type); |
| } |
| |
| protected void writeConvertToTypes(Binder binder, TypeConverterBinding element) { |
| binder.withSource(element.getSource()) |
| .convertToTypes(element.getTypeMatcher(), element.getTypeConverter()); |
| } |
| |
| protected <T> void writeBind(Binder binder, Binding<T> element) { |
| ScopedBindingBuilder sbb |
| = bindKeyToTarget(element, binder.withSource(element.getSource()), element.getKey()); |
| applyScoping(element, sbb); |
| } |
| |
| /** |
| * Writes the elements of the private environment to a new private binder and {@link |
| * #setPrivateBinder associates} the two. |
| */ |
| protected void writePrivateElements(Binder binder, PrivateElements element) { |
| PrivateBinder privateBinder = binder.withSource(element.getSource()).newPrivateBinder(); |
| setPrivateBinder(element, privateBinder); |
| apply(privateBinder, element.getElements()); |
| } |
| |
| /** |
| * Execute this target against the linked binding builder. |
| */ |
| protected <T> ScopedBindingBuilder bindKeyToTarget( |
| final Binding<T> binding, final Binder binder, final Key<T> key) { |
| return binding.acceptTargetVisitor(new BindingTargetVisitor<T, ScopedBindingBuilder>() { |
| public ScopedBindingBuilder visit(InstanceBinding<? extends T> binding) { |
| binder.bind(key).toInstance(binding.getInstance()); |
| return null; |
| } |
| |
| public ScopedBindingBuilder visit( |
| ProviderInstanceBinding<? extends T> binding) { |
| return binder.bind(key).toProvider(binding.getProviderInstance()); |
| } |
| |
| public ScopedBindingBuilder visit(ProviderKeyBinding<? extends T> binding) { |
| return binder.bind(key).toProvider(binding.getProviderKey()); |
| } |
| |
| public ScopedBindingBuilder visit(LinkedKeyBinding<? extends T> binding) { |
| return binder.bind(key).to(binding.getLinkedKey()); |
| } |
| |
| public ScopedBindingBuilder visit(UntargettedBinding<? extends T> binding) { |
| return binder.bind(key); |
| } |
| |
| public ScopedBindingBuilder visit(ExposedBinding<? extends T> binding) { |
| PrivateBinder privateBinder = getPrivateBinder(binding.getPrivateElements()); |
| privateBinder.withSource(binding.getSource()).expose(key); |
| return null; |
| } |
| |
| public ScopedBindingBuilder visit( |
| ConvertedConstantBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Non-module element"); |
| } |
| |
| public ScopedBindingBuilder visit(ConstructorBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Non-module element"); |
| } |
| |
| public ScopedBindingBuilder visit(ProviderBinding<? extends T> binding) { |
| throw new IllegalArgumentException("Non-module element"); |
| } |
| }); |
| } |
| |
| /** Associates {@code binder} with {@code privateElements}. */ |
| protected void setPrivateBinder(PrivateElements privateElements, PrivateBinder binder) { |
| checkArgument(!environmentToBinder.containsKey(privateElements), |
| "A private binder already exists for %s", privateElements); |
| environmentToBinder.put(privateElements, binder); |
| } |
| |
| /** |
| * Returns the {@code binder} accociated with {@code privateElements}. This can be used to |
| * expose bindings to the enclosing environment. |
| */ |
| protected PrivateBinder getPrivateBinder(PrivateElements privateElements) { |
| PrivateBinder privateBinder = environmentToBinder.get(privateElements); |
| checkArgument(privateBinder != null, "No private binder for %s", privateElements); |
| return privateBinder; |
| } |
| |
| protected void applyScoping(Binding<?> binding, final ScopedBindingBuilder scopedBindingBuilder) { |
| binding.acceptScopingVisitor(new BindingScopingVisitor<Void>() { |
| public Void visitEagerSingleton() { |
| scopedBindingBuilder.asEagerSingleton(); |
| return null; |
| } |
| |
| public Void visitScope(Scope scope) { |
| scopedBindingBuilder.in(scope); |
| return null; |
| } |
| |
| public Void visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { |
| scopedBindingBuilder.in(scopeAnnotation); |
| return null; |
| } |
| |
| public Void visitNoScoping() { |
| // do nothing |
| return null; |
| } |
| }); |
| } |
| |
| protected <T> void writeGetProvider(Binder binder, ProviderLookup<T> element) { |
| Provider<T> provider = binder.withSource(element.getSource()).getProvider(element.getKey()); |
| element.initializeDelegate(provider); |
| } |
| |
| protected <T> void writeGetMembersInjector(Binder binder, MembersInjectorLookup<T> element) { |
| MembersInjector<T> membersInjector |
| = binder.withSource(element.getSource()).getMembersInjector(element.getTypeLiteral()); |
| element.initializeDelegate(membersInjector); |
| } |
| } |