/** * 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 static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.Maps; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Scope; import com.google.inject.PrivateBinder; import com.google.inject.binder.ScopedBindingBuilder; import java.lang.annotation.Annotation; import java.util.List; import java.util.Map; import org.aopalliance.intercept.MethodInterceptor; /** * Creates a Module from a collection of component elements. * * @author jessewilson@google.com (Jesse Wilson) * @since 2.0 */ public class ModuleWriter { private final Map environmentToBinder = Maps.newHashMap(); /** * Returns a module that executes the specified elements using this executing visitor. */ public final Module create(final Iterable 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 elements) { checkNotNull(binder, "binder"); checkNotNull(elements, "elements"); ElementVisitor visitor = new ElementVisitor() { public Void visitMessage(Message message) { writeMessage(binder, message); return null; } public Void visitInterceptorBinding(InterceptorBinding element) { writeBindInterceptor(binder, element); return null; } public Void visitScopeBinding(ScopeBinding element) { writeBindScope(binder, element); return null; } public Void visitInjectionRequest(InjectionRequest element) { writeRequestInjection(binder, element); return null; } public Void visitStaticInjectionRequest(StaticInjectionRequest element) { writeRequestStaticInjection(binder, element); return null; } public Void visitTypeConverterBinding(TypeConverterBinding element) { writeConvertToTypes(binder, element); return null; } public Void visitBinding(Binding element) { writeBind(binder, element); return null; } public Void visitProviderLookup(ProviderLookup element) { writeGetProvider(binder, element); return null; } public Void visitPrivateElements(PrivateElements privateElements) { writePrivateElements(binder, privateElements); return null; } }; for (Element element : elements) { element.acceptVisitor(visitor); } } protected void writeMessage(Binder binder, Message element) { binder.addError(element); } protected void writeBindInterceptor(Binder binder, InterceptorBinding element) { List interceptors = element.getInterceptors(); binder.withSource(element.getSource()).bindInterceptor( element.getClassMatcher(), element.getMethodMatcher(), interceptors.toArray(new MethodInterceptor[interceptors.size()])); } 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 void writeBind(Binder binder, Binding 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 ScopedBindingBuilder bindKeyToTarget( final Binding binding, final Binder binder, final Key key) { return binding.acceptTargetVisitor(new BindingTargetVisitor() { public ScopedBindingBuilder visitInstance(InstanceBinding binding) { binder.bind(key).toInstance(binding.getInstance()); return null; } public ScopedBindingBuilder visitProviderInstance( ProviderInstanceBinding binding) { return binder.bind(key).toProvider(binding.getProviderInstance()); } public ScopedBindingBuilder visitProviderKey(ProviderKeyBinding binding) { return binder.bind(key).toProvider(binding.getProviderKey()); } public ScopedBindingBuilder visitLinkedKey(LinkedKeyBinding binding) { return binder.bind(key).to(binding.getLinkedKey()); } public ScopedBindingBuilder visitUntargetted(UntargettedBinding binding) { return binder.bind(key); } public ScopedBindingBuilder visitExposed(ExposedBinding binding) { PrivateBinder privateBinder = getPrivateBinder(binding.getPrivateElements()); privateBinder.withSource(binding.getSource()).expose(key); return null; } public ScopedBindingBuilder visitConvertedConstant( ConvertedConstantBinding binding) { throw new IllegalArgumentException("Non-module element"); } public ScopedBindingBuilder visitConstructor(ConstructorBinding binding) { throw new IllegalArgumentException("Non-module element"); } public ScopedBindingBuilder visitProviderBinding(ProviderBinding 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() { public Void visitEagerSingleton() { scopedBindingBuilder.asEagerSingleton(); return null; } public Void visitScope(Scope scope) { scopedBindingBuilder.in(scope); return null; } public Void visitScopeAnnotation(Class scopeAnnotation) { scopedBindingBuilder.in(scopeAnnotation); return null; } public Void visitNoScoping() { // do nothing return null; } }); } protected void writeGetProvider(Binder binder, ProviderLookup element) { Provider provider = binder.withSource(element.getSource()).getProvider(element.getKey()); element.initDelegate(provider); } }