blob: 03871a791c110d5d97b2e47a5d255cf906b43419 [file] [log] [blame]
limpbizkit477f9f92008-07-28 07:05:14 +00001/**
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.spi;
18
limpbizkit477f9f92008-07-28 07:05:14 +000019import com.google.inject.AbstractModule;
20import com.google.inject.Binder;
limpbizkit76c24b12008-12-25 04:32:41 +000021import com.google.inject.Binding;
limpbizkit477f9f92008-07-28 07:05:14 +000022import com.google.inject.Key;
limpbizkit97eac0f2009-03-28 18:25:35 +000023import com.google.inject.MembersInjector;
limpbizkit477f9f92008-07-28 07:05:14 +000024import com.google.inject.Module;
limpbizkitbf0d8762009-02-19 09:06:22 +000025import com.google.inject.PrivateBinder;
limpbizkitfcbdf992008-11-26 02:37:35 +000026import com.google.inject.PrivateModule;
limpbizkit477f9f92008-07-28 07:05:14 +000027import com.google.inject.Provider;
28import com.google.inject.Scope;
29import com.google.inject.Stage;
30import com.google.inject.TypeLiteral;
31import com.google.inject.binder.AnnotatedBindingBuilder;
32import com.google.inject.binder.AnnotatedConstantBindingBuilder;
limpbizkit5ea4ab22008-11-25 08:43:49 +000033import com.google.inject.binder.AnnotatedElementBuilder;
limpbizkit76c24b12008-12-25 04:32:41 +000034import com.google.inject.internal.AbstractBindingBuilder;
35import com.google.inject.internal.BindingBuilder;
36import com.google.inject.internal.ConstantBindingBuilderImpl;
limpbizkit72d11dd2008-11-02 07:59:13 +000037import com.google.inject.internal.Errors;
limpbizkit53664a72009-02-21 00:25:27 +000038import com.google.inject.internal.ImmutableList;
39import com.google.inject.internal.Lists;
40import static com.google.inject.internal.Preconditions.checkArgument;
limpbizkitc3f92842008-12-30 19:43:47 +000041import com.google.inject.internal.PrivateElementsImpl;
limpbizkit86cb3bb2008-10-15 21:25:50 +000042import com.google.inject.internal.ProviderMethodsModule;
limpbizkit53664a72009-02-21 00:25:27 +000043import com.google.inject.internal.Sets;
limpbizkit477f9f92008-07-28 07:05:14 +000044import com.google.inject.internal.SourceProvider;
limpbizkitaa07ab02009-05-15 07:10:43 +000045import com.google.inject.internal.ExposureBuilder;
limpbizkit477f9f92008-07-28 07:05:14 +000046import com.google.inject.matcher.Matcher;
47import java.lang.annotation.Annotation;
48import java.lang.reflect.Method;
49import java.util.Arrays;
limpbizkit5ea4ab22008-11-25 08:43:49 +000050import java.util.Collection;
limpbizkit477f9f92008-07-28 07:05:14 +000051import java.util.Collections;
52import java.util.List;
53import java.util.Set;
limpbizkit477f9f92008-07-28 07:05:14 +000054
55/**
limpbizkitaa07ab02009-05-15 07:10:43 +000056 * Exposes elements of a module so they can be inspected, validated or {@link
57 * Element#applyTo(Binder) rewritten}.
limpbizkit477f9f92008-07-28 07:05:14 +000058 *
59 * @author jessewilson@google.com (Jesse Wilson)
limpbizkitc489adf2008-11-18 07:01:33 +000060 * @since 2.0
limpbizkit477f9f92008-07-28 07:05:14 +000061 */
62public final class Elements {
limpbizkitafa4b5d2008-08-02 18:40:47 +000063 private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
64 = new DefaultBindingTargetVisitor<Object, Object>() {
limpbizkit03b81a62009-03-18 05:34:39 +000065 @Override public Object visit(InstanceBinding<?> binding) {
limpbizkit76c24b12008-12-25 04:32:41 +000066 return binding.getInstance();
limpbizkit477f9f92008-07-28 07:05:14 +000067 }
68
limpbizkit8996e802008-12-28 01:44:29 +000069 @Override protected Object visitOther(Binding<?> binding) {
limpbizkit477f9f92008-07-28 07:05:14 +000070 throw new IllegalArgumentException();
71 }
72 };
73
74 /**
75 * Records the elements executed by {@code modules}.
76 */
77 public static List<Element> getElements(Module... modules) {
78 return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
79 }
80
81 /**
82 * Records the elements executed by {@code modules}.
83 */
84 public static List<Element> getElements(Stage stage, Module... modules) {
85 return getElements(stage, Arrays.asList(modules));
86 }
87
88 /**
89 * Records the elements executed by {@code modules}.
90 */
91 public static List<Element> getElements(Iterable<? extends Module> modules) {
92 return getElements(Stage.DEVELOPMENT, modules);
93 }
94
95 /**
96 * Records the elements executed by {@code modules}.
97 */
98 public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) {
99 RecordingBinder binder = new RecordingBinder(stage);
100 for (Module module : modules) {
101 binder.install(module);
102 }
103 return Collections.unmodifiableList(binder.elements);
104 }
105
limpbizkitaa07ab02009-05-15 07:10:43 +0000106 /**
107 * Returns the module composed of {@code elements}.
108 */
109 public static Module getModule(final Iterable<? extends Element> elements) {
110 return new Module() {
111 public void configure(Binder binder) {
112 for (Element element : elements) {
113 element.applyTo(binder);
114 }
115 }
116 };
117 }
118
limpbizkit477f9f92008-07-28 07:05:14 +0000119 @SuppressWarnings("unchecked")
limpbizkit7e1e4f72008-08-02 21:21:32 +0000120 static <T> BindingTargetVisitor<T, T> getInstanceVisitor() {
limpbizkitafa4b5d2008-08-02 18:40:47 +0000121 return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
limpbizkit477f9f92008-07-28 07:05:14 +0000122 }
123
limpbizkit5ea4ab22008-11-25 08:43:49 +0000124 private static class RecordingBinder implements Binder, PrivateBinder {
limpbizkit477f9f92008-07-28 07:05:14 +0000125 private final Stage stage;
126 private final Set<Module> modules;
127 private final List<Element> elements;
128 private final Object source;
129 private final SourceProvider sourceProvider;
limpbizkitfcbdf992008-11-26 02:37:35 +0000130
131 /** The binder where exposed bindings will be created */
132 private final RecordingBinder parent;
limpbizkitc3f92842008-12-30 19:43:47 +0000133 private final PrivateElementsImpl privateElements;
limpbizkit477f9f92008-07-28 07:05:14 +0000134
135 private RecordingBinder(Stage stage) {
136 this.stage = stage;
137 this.modules = Sets.newHashSet();
138 this.elements = Lists.newArrayList();
139 this.source = null;
limpbizkit5ae41eb2009-06-06 17:51:27 +0000140 this.sourceProvider = SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses(
limpbizkit76c24b12008-12-25 04:32:41 +0000141 Elements.class, RecordingBinder.class, AbstractModule.class,
142 ConstantBindingBuilderImpl.class, AbstractBindingBuilder.class, BindingBuilder.class);
limpbizkitfcbdf992008-11-26 02:37:35 +0000143 this.parent = null;
limpbizkitc3f92842008-12-30 19:43:47 +0000144 this.privateElements = null;
limpbizkit477f9f92008-07-28 07:05:14 +0000145 }
146
limpbizkitfcbdf992008-11-26 02:37:35 +0000147 /** Creates a recording binder that's backed by {@code prototype}. */
148 private RecordingBinder(
149 RecordingBinder prototype, Object source, SourceProvider sourceProvider) {
limpbizkit477f9f92008-07-28 07:05:14 +0000150 checkArgument(source == null ^ sourceProvider == null);
151
limpbizkitfcbdf992008-11-26 02:37:35 +0000152 this.stage = prototype.stage;
153 this.modules = prototype.modules;
154 this.elements = prototype.elements;
limpbizkit477f9f92008-07-28 07:05:14 +0000155 this.source = source;
156 this.sourceProvider = sourceProvider;
limpbizkitfcbdf992008-11-26 02:37:35 +0000157 this.parent = prototype.parent;
limpbizkitc3f92842008-12-30 19:43:47 +0000158 this.privateElements = prototype.privateElements;
limpbizkit5ea4ab22008-11-25 08:43:49 +0000159 }
160
161 /** Creates a private recording binder. */
limpbizkitc3f92842008-12-30 19:43:47 +0000162 private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) {
limpbizkit5ea4ab22008-11-25 08:43:49 +0000163 this.stage = parent.stage;
164 this.modules = Sets.newHashSet();
limpbizkitc3f92842008-12-30 19:43:47 +0000165 this.elements = privateElements.getElementsMutable();
limpbizkit5ea4ab22008-11-25 08:43:49 +0000166 this.source = parent.source;
167 this.sourceProvider = parent.sourceProvider;
limpbizkitfcbdf992008-11-26 02:37:35 +0000168 this.parent = parent;
limpbizkitc3f92842008-12-30 19:43:47 +0000169 this.privateElements = privateElements;
limpbizkit477f9f92008-07-28 07:05:14 +0000170 }
171
limpbizkitbf0d8762009-02-19 09:06:22 +0000172 /*if[AOP]*/
limpbizkit477f9f92008-07-28 07:05:14 +0000173 public void bindInterceptor(
174 Matcher<? super Class<?>> classMatcher,
175 Matcher<? super Method> methodMatcher,
limpbizkit4f6274a2009-02-19 21:57:55 +0000176 org.aopalliance.intercept.MethodInterceptor... interceptors) {
limpbizkit00ca9f72008-08-02 17:56:17 +0000177 elements.add(new InterceptorBinding(getSource(), classMatcher, methodMatcher, interceptors));
limpbizkit477f9f92008-07-28 07:05:14 +0000178 }
limpbizkitbf0d8762009-02-19 09:06:22 +0000179 /*end[AOP]*/
limpbizkit477f9f92008-07-28 07:05:14 +0000180
181 public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
limpbizkit00ca9f72008-08-02 17:56:17 +0000182 elements.add(new ScopeBinding(getSource(), annotationType, scope));
limpbizkit477f9f92008-07-28 07:05:14 +0000183 }
184
limpbizkit03b81a62009-03-18 05:34:39 +0000185 @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
186 public void requestInjection(Object instance) {
187 requestInjection((TypeLiteral) TypeLiteral.get(instance.getClass()), instance);
188 }
189
190 public <T> void requestInjection(TypeLiteral<T> type, T instance) {
191 elements.add(new InjectionRequest<T>(getSource(), type, instance));
192 }
193
limpbizkit97eac0f2009-03-28 18:25:35 +0000194 public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) {
195 final MembersInjectorLookup<T> element
196 = new MembersInjectorLookup<T>(getSource(), typeLiteral);
197 elements.add(element);
limpbizkit8d620752009-03-31 22:37:26 +0000198 return element.getMembersInjector();
limpbizkit03b81a62009-03-18 05:34:39 +0000199 }
200
201 public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
limpbizkit97eac0f2009-03-28 18:25:35 +0000202 return getMembersInjector(TypeLiteral.get(type));
limpbizkit03b81a62009-03-18 05:34:39 +0000203 }
204
limpbizkita843a952009-04-08 22:24:55 +0000205 public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
limpbizkitee792462009-04-08 23:48:49 +0000206 elements.add(new TypeListenerBinding(getSource(), listener, typeMatcher));
limpbizkit477f9f92008-07-28 07:05:14 +0000207 }
208
209 public void requestStaticInjection(Class<?>... types) {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000210 for (Class<?> type : types) {
211 elements.add(new StaticInjectionRequest(getSource(), type));
212 }
limpbizkit477f9f92008-07-28 07:05:14 +0000213 }
214
215 public void install(Module module) {
216 if (modules.add(module)) {
limpbizkitfcbdf992008-11-26 02:37:35 +0000217 Binder binder = this;
218 if (module instanceof PrivateModule) {
219 binder = binder.newPrivateBinder();
220 }
221
limpbizkit477f9f92008-07-28 07:05:14 +0000222 try {
limpbizkitfcbdf992008-11-26 02:37:35 +0000223 module.configure(binder);
limpbizkit477f9f92008-07-28 07:05:14 +0000224 } catch (RuntimeException e) {
limpbizkit72d11dd2008-11-02 07:59:13 +0000225 Collection<Message> messages = Errors.getMessagesFromThrowable(e);
226 if (!messages.isEmpty()) {
227 elements.addAll(messages);
228 } else {
229 addError(e);
230 }
limpbizkit477f9f92008-07-28 07:05:14 +0000231 }
limpbizkitfcbdf992008-11-26 02:37:35 +0000232 binder.install(ProviderMethodsModule.forModule(module));
limpbizkit477f9f92008-07-28 07:05:14 +0000233 }
234 }
235
236 public Stage currentStage() {
237 return stage;
238 }
239
240 public void addError(String message, Object... arguments) {
limpbizkitb1f42f52008-11-26 07:44:53 +0000241 elements.add(new Message(getSource(), Errors.format(message, arguments)));
limpbizkit477f9f92008-07-28 07:05:14 +0000242 }
243
244 public void addError(Throwable t) {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000245 String message = "An exception was caught and reported. Message: " + t.getMessage();
246 elements.add(new Message(ImmutableList.of(getSource()), message, t));
limpbizkit477f9f92008-07-28 07:05:14 +0000247 }
248
249 public void addError(Message message) {
250 elements.add(message);
251 }
252
limpbizkitd1fe1302008-08-01 06:40:18 +0000253 public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) {
limpbizkit76c24b12008-12-25 04:32:41 +0000254 return new BindingBuilder<T>(this, elements, getSource(), key);
limpbizkit477f9f92008-07-28 07:05:14 +0000255 }
256
257 public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
258 return bind(Key.get(typeLiteral));
259 }
260
261 public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
262 return bind(Key.get(type));
263 }
264
265 public AnnotatedConstantBindingBuilder bindConstant() {
limpbizkit76c24b12008-12-25 04:32:41 +0000266 return new ConstantBindingBuilderImpl<Void>(this, elements, getSource());
limpbizkit477f9f92008-07-28 07:05:14 +0000267 }
268
269 public <T> Provider<T> getProvider(final Key<T> key) {
limpbizkit97eac0f2009-03-28 18:25:35 +0000270 final ProviderLookup<T> element = new ProviderLookup<T>(getSource(), key);
271 elements.add(element);
limpbizkit8d620752009-03-31 22:37:26 +0000272 return element.getProvider();
limpbizkit477f9f92008-07-28 07:05:14 +0000273 }
274
275 public <T> Provider<T> getProvider(Class<T> type) {
276 return getProvider(Key.get(type));
277 }
278
279 public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
280 TypeConverter converter) {
limpbizkit00ca9f72008-08-02 17:56:17 +0000281 elements.add(new TypeConverterBinding(getSource(), typeMatcher, converter));
limpbizkit477f9f92008-07-28 07:05:14 +0000282 }
283
limpbizkit5ea4ab22008-11-25 08:43:49 +0000284 public RecordingBinder withSource(final Object source) {
limpbizkit477f9f92008-07-28 07:05:14 +0000285 return new RecordingBinder(this, source, null);
286 }
287
limpbizkit5ea4ab22008-11-25 08:43:49 +0000288 public RecordingBinder skipSources(Class... classesToSkip) {
limpbizkit477f9f92008-07-28 07:05:14 +0000289 // if a source is specified explicitly, we don't need to skip sources
290 if (source != null) {
291 return this;
292 }
293
294 SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
295 return new RecordingBinder(this, null, newSourceProvider);
296 }
297
limpbizkit5ea4ab22008-11-25 08:43:49 +0000298 public PrivateBinder newPrivateBinder() {
limpbizkitc3f92842008-12-30 19:43:47 +0000299 PrivateElementsImpl privateElements = new PrivateElementsImpl(getSource());
300 elements.add(privateElements);
301 return new RecordingBinder(this, privateElements);
limpbizkit5ea4ab22008-11-25 08:43:49 +0000302 }
303
limpbizkit4994bf62008-12-27 02:54:59 +0000304 public void expose(Key<?> key) {
305 exposeInternal(key);
limpbizkit5ea4ab22008-11-25 08:43:49 +0000306 }
307
308 public AnnotatedElementBuilder expose(Class<?> type) {
309 return exposeInternal(Key.get(type));
310 }
311
312 public AnnotatedElementBuilder expose(TypeLiteral<?> type) {
313 return exposeInternal(Key.get(type));
314 }
315
limpbizkitfcbdf992008-11-26 02:37:35 +0000316 private <T> AnnotatedElementBuilder exposeInternal(Key<T> key) {
limpbizkitc3f92842008-12-30 19:43:47 +0000317 if (privateElements == null) {
limpbizkitb1f42f52008-11-26 07:44:53 +0000318 addError("Cannot expose %s on a standard binder. "
319 + "Exposed bindings are only applicable to private binders.", key);
320 return new AnnotatedElementBuilder() {
321 public void annotatedWith(Class<? extends Annotation> annotationType) {}
322 public void annotatedWith(Annotation annotation) {}
323 };
limpbizkit5ea4ab22008-11-25 08:43:49 +0000324 }
325
limpbizkitaa07ab02009-05-15 07:10:43 +0000326 ExposureBuilder<T> builder = new ExposureBuilder<T>(this, getSource(), key);
limpbizkitc3f92842008-12-30 19:43:47 +0000327 privateElements.addExposureBuilder(builder);
limpbizkitfcbdf992008-11-26 02:37:35 +0000328 return builder;
limpbizkit5ea4ab22008-11-25 08:43:49 +0000329 }
330
limpbizkit477f9f92008-07-28 07:05:14 +0000331 protected Object getSource() {
332 return sourceProvider != null
333 ? sourceProvider.get()
334 : source;
335 }
336
337 @Override public String toString() {
338 return "Binder";
339 }
340 }
341}