1/**
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
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
21
22import com.google.common.collect.ImmutableList;
23import com.google.common.collect.Lists;
24import com.google.common.collect.Maps;
25import com.google.common.collect.Sets;
26import com.google.inject.AbstractModule;
27import com.google.inject.Binder;
28import com.google.inject.Binding;
29import com.google.inject.Key;
30import com.google.inject.MembersInjector;
31import com.google.inject.Module;
32import com.google.inject.PrivateBinder;
33import com.google.inject.PrivateModule;
34import com.google.inject.Provider;
35import com.google.inject.Scope;
36import com.google.inject.Stage;
37import com.google.inject.TypeLiteral;
38import com.google.inject.binder.AnnotatedBindingBuilder;
39import com.google.inject.binder.AnnotatedConstantBindingBuilder;
40import com.google.inject.binder.AnnotatedElementBuilder;
41import com.google.inject.internal.AbstractBindingBuilder;
42import com.google.inject.internal.BindingBuilder;
43import com.google.inject.internal.ConstantBindingBuilderImpl;
44import com.google.inject.internal.Errors;
45import com.google.inject.internal.ExposureBuilder;
46import com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
47import com.google.inject.internal.MoreTypes;
48import com.google.inject.internal.PrivateElementsImpl;
49import com.google.inject.internal.ProviderMethodsModule;
50import com.google.inject.internal.util.SourceProvider;
51import com.google.inject.internal.util.StackTraceElements;
52import com.google.inject.matcher.Matcher;
53
54import java.lang.annotation.Annotation;
55import java.lang.reflect.Method;
56import java.util.Arrays;
57import java.util.Collection;
58import java.util.Collections;
59import java.util.List;
60import java.util.Map;
61import java.util.Set;
62
63/**
64 * Exposes elements of a module so they can be inspected, validated or {@link
65 * Element#applyTo(Binder) rewritten}.
66 *
67 * @author jessewilson@google.com (Jesse Wilson)
68 * @since 2.0
69 */
70public final class Elements {
71
72  private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR
73      = new DefaultBindingTargetVisitor<Object, Object>() {
74    @Override public Object visit(InstanceBinding<?> binding) {
75      return binding.getInstance();
76    }
77
78    @Override protected Object visitOther(Binding<?> binding) {
79      throw new IllegalArgumentException();
80    }
81  };
82
83  /**
84   * Records the elements executed by {@code modules}.
85   */
86  public static List<Element> getElements(Module... modules) {
87    return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
88  }
89
90  /**
91   * Records the elements executed by {@code modules}.
92   */
93  public static List<Element> getElements(Stage stage, Module... modules) {
94    return getElements(stage, Arrays.asList(modules));
95  }
96
97  /**
98   * Records the elements executed by {@code modules}.
99   */
100  public static List<Element> getElements(Iterable<? extends Module> modules) {
101    return getElements(Stage.DEVELOPMENT, modules);
102  }
103
104  /**
105   * Records the elements executed by {@code modules}.
106   */
107  public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) {
108    RecordingBinder binder = new RecordingBinder(stage);
109    for (Module module : modules) {
110      binder.install(module);
111    }
112    binder.scanForAnnotatedMethods();
113    for (RecordingBinder child : binder.privateBinders) {
114      child.scanForAnnotatedMethods();
115    }
116    // Free the memory consumed by the stack trace elements cache
117    StackTraceElements.clearCache();
118    return Collections.unmodifiableList(binder.elements);
119  }
120
121  private static class ElementsAsModule implements Module {
122    private final Iterable<? extends Element> elements;
123
124    ElementsAsModule(Iterable<? extends Element> elements) {
125      this.elements = elements;
126    }
127
128    @Override
129    public void configure(Binder binder) {
130      for (Element element : elements) {
131        element.applyTo(binder);
132      }
133    }
134  }
135
136  /**
137   * Returns the module composed of {@code elements}.
138   */
139  public static Module getModule(final Iterable<? extends Element> elements) {
140    return new ElementsAsModule(elements);
141  }
142
143  @SuppressWarnings("unchecked")
144  static <T> BindingTargetVisitor<T, T> getInstanceVisitor() {
145    return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
146  }
147
148  private static class ModuleInfo {
149    private final Binder binder;
150    private final ModuleSource moduleSource;
151    private final boolean skipScanning;
152
153    private ModuleInfo(Binder binder, ModuleSource moduleSource, boolean skipScanning) {
154      this.binder = binder;
155      this.moduleSource = moduleSource;
156      this.skipScanning = skipScanning;
157    }
158  }
159
160  private static class RecordingBinder implements Binder, PrivateBinder {
161    private final Stage stage;
162    private final Map<Module, ModuleInfo> modules;
163    private final List<Element> elements;
164    private final Object source;
165    /** The current modules stack */
166    private ModuleSource moduleSource = null;
167    private final SourceProvider sourceProvider;
168    private final Set<ModuleAnnotatedMethodScanner> scanners;
169
170    /** The binder where exposed bindings will be created */
171    private final RecordingBinder parent;
172    private final PrivateElementsImpl privateElements;
173
174    /** All children private binders, so we can scan through them. */
175    private final List<RecordingBinder> privateBinders;
176
177    private RecordingBinder(Stage stage) {
178      this.stage = stage;
179      this.modules = Maps.newLinkedHashMap();
180      this.scanners = Sets.newLinkedHashSet();
181      this.elements = Lists.newArrayList();
182      this.source = null;
183      this.sourceProvider = SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses(
184          Elements.class, RecordingBinder.class, AbstractModule.class,
185          ConstantBindingBuilderImpl.class, AbstractBindingBuilder.class, BindingBuilder.class);
186      this.parent = null;
187      this.privateElements = null;
188      this.privateBinders = Lists.newArrayList();
189    }
190
191    /** Creates a recording binder that's backed by {@code prototype}. */
192    private RecordingBinder(
193        RecordingBinder prototype, Object source, SourceProvider sourceProvider) {
194      checkArgument(source == null ^ sourceProvider == null);
195
196      this.stage = prototype.stage;
197      this.modules = prototype.modules;
198      this.elements = prototype.elements;
199      this.scanners = prototype.scanners;
200      this.source = source;
201      this.moduleSource = prototype.moduleSource;
202      this.sourceProvider = sourceProvider;
203      this.parent = prototype.parent;
204      this.privateElements = prototype.privateElements;
205      this.privateBinders = prototype.privateBinders;
206    }
207
208    /** Creates a private recording binder. */
209    private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) {
210      this.stage = parent.stage;
211      this.modules = Maps.newLinkedHashMap();
212      this.scanners = Sets.newLinkedHashSet(parent.scanners);
213      this.elements = privateElements.getElementsMutable();
214      this.source = parent.source;
215      this.moduleSource = parent.moduleSource;
216      this.sourceProvider = parent.sourceProvider;
217      this.parent = parent;
218      this.privateElements = privateElements;
219      this.privateBinders = parent.privateBinders;
220    }
221
222    /*if[AOP]*/
223    @Override
224    public void bindInterceptor(
225        Matcher<? super Class<?>> classMatcher,
226        Matcher<? super Method> methodMatcher,
227        org.aopalliance.intercept.MethodInterceptor... interceptors) {
228      elements.add(new InterceptorBinding(
229          getElementSource(), classMatcher, methodMatcher, interceptors));
230    }
231    /*end[AOP]*/
232
233    @Override
234    public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
235      elements.add(new ScopeBinding(getElementSource(), annotationType, scope));
236    }
237
238    @Override
239    @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
240    public void requestInjection(Object instance) {
241      requestInjection((TypeLiteral<Object>) TypeLiteral.get(instance.getClass()), instance);
242    }
243
244    @Override
245    public <T> void requestInjection(TypeLiteral<T> type, T instance) {
246      elements.add(new InjectionRequest<T>(getElementSource(), MoreTypes.canonicalizeForKey(type),
247          instance));
248    }
249
250    @Override
251    public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) {
252      final MembersInjectorLookup<T> element = new MembersInjectorLookup<T>(getElementSource(),
253          MoreTypes.canonicalizeForKey(typeLiteral));
254      elements.add(element);
255      return element.getMembersInjector();
256    }
257
258    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
259      return getMembersInjector(TypeLiteral.get(type));
260    }
261
262    public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
263      elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher));
264    }
265
266    public void bindListener(Matcher<? super Binding<?>> bindingMatcher,
267        ProvisionListener... listeners) {
268      elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners));
269    }
270
271    public void requestStaticInjection(Class<?>... types) {
272      for (Class<?> type : types) {
273        elements.add(new StaticInjectionRequest(getElementSource(), type));
274      }
275    }
276
277    /**
278     * Applies all scanners to the modules we've installed. We skip certain
279     * PrivateModules because store them in more than one Modules map and only
280     * want to process them through one of the maps.  (They're stored in both
281     * maps to prevent a module from being installed more than once.)
282     */
283    void scanForAnnotatedMethods() {
284      for (ModuleAnnotatedMethodScanner scanner : scanners) {
285        // Note: we must iterate over a copy of the modules because calling install(..)
286        // will mutate modules, otherwise causing a ConcurrentModificationException.
287        for (Map.Entry<Module, ModuleInfo> entry : Maps.newLinkedHashMap(modules).entrySet()) {
288          Module module = entry.getKey();
289          ModuleInfo info = entry.getValue();
290          if (info.skipScanning) {
291            continue;
292          }
293          moduleSource = entry.getValue().moduleSource;
294          try {
295            info.binder.install(ProviderMethodsModule.forModule(module, scanner));
296          } catch(RuntimeException e) {
297            Collection<Message> messages = Errors.getMessagesFromThrowable(e);
298            if (!messages.isEmpty()) {
299              elements.addAll(messages);
300            } else {
301              addError(e);
302            }
303          }
304        }
305      }
306      moduleSource = null;
307    }
308
309    public void install(Module module) {
310      if (!modules.containsKey(module)) {
311        RecordingBinder binder = this;
312        boolean unwrapModuleSource = false;
313        // Update the module source for the new module
314        if (module instanceof ProviderMethodsModule) {
315          // There are two reason's we'd want to get the module source in a ProviderMethodsModule.
316          // ModuleAnnotatedMethodScanner lets users scan their own modules for @Provides-like
317          // bindings.  If they install the module at a top-level, then moduleSource can be null.
318          // Also, if they pass something other than 'this' to it, we'd have the wrong source.
319          Object delegate = ((ProviderMethodsModule) module).getDelegateModule();
320          if (moduleSource == null
321              || !moduleSource.getModuleClassName().equals(delegate.getClass().getName())) {
322            moduleSource = getModuleSource(delegate);
323            unwrapModuleSource = true;
324          }
325        } else {
326          moduleSource = getModuleSource(module);
327          unwrapModuleSource = true;
328        }
329        boolean skipScanning = false;
330        if (module instanceof PrivateModule) {
331          binder = (RecordingBinder) binder.newPrivateBinder();
332          // Store the module in the private binder too so we scan for it.
333          binder.modules.put(module, new ModuleInfo(binder, moduleSource, false));
334          skipScanning = true; // don't scan this module in the parent's module set.
335        }
336        // Always store this in the parent binder (even if it was a private module)
337        // so that we know not to process it again, and so that scanners inherit down.
338        modules.put(module, new ModuleInfo(binder, moduleSource, skipScanning));
339        try {
340          module.configure(binder);
341        } catch (RuntimeException e) {
342          Collection<Message> messages = Errors.getMessagesFromThrowable(e);
343          if (!messages.isEmpty()) {
344            elements.addAll(messages);
345          } else {
346            addError(e);
347          }
348        }
349        binder.install(ProviderMethodsModule.forModule(module));
350        // We are done with this module, so undo module source change
351        if (unwrapModuleSource) {
352          moduleSource = moduleSource.getParent();
353        }
354      }
355    }
356
357    public Stage currentStage() {
358      return stage;
359    }
360
361    public void addError(String message, Object... arguments) {
362      elements.add(new Message(getElementSource(), Errors.format(message, arguments)));
363    }
364
365    public void addError(Throwable t) {
366      String message = "An exception was caught and reported. Message: " + t.getMessage();
367      elements.add(new Message(ImmutableList.of((Object) getElementSource()), message, t));
368    }
369
370    public void addError(Message message) {
371      elements.add(message);
372    }
373
374    public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) {
375      BindingBuilder<T> builder =
376          new BindingBuilder<T>(this, elements, getElementSource(), MoreTypes.canonicalizeKey(key));
377      return builder;
378    }
379
380    public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
381      return bind(Key.get(typeLiteral));
382    }
383
384    public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
385      return bind(Key.get(type));
386    }
387
388    public AnnotatedConstantBindingBuilder bindConstant() {
389      return new ConstantBindingBuilderImpl<Void>(this, elements, getElementSource());
390    }
391
392    public <T> Provider<T> getProvider(final Key<T> key) {
393      return getProvider(Dependency.get(key));
394    }
395
396    public <T> Provider<T> getProvider(final Dependency<T> dependency) {
397      final ProviderLookup<T> element = new ProviderLookup<T>(getElementSource(), dependency);
398      elements.add(element);
399      return element.getProvider();
400    }
401
402    public <T> Provider<T> getProvider(Class<T> type) {
403      return getProvider(Key.get(type));
404    }
405
406    public void convertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
407        TypeConverter converter) {
408      elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter));
409    }
410
411    public RecordingBinder withSource(final Object source) {
412      return source == this.source ? this : new RecordingBinder(this, source, null);
413    }
414
415    public RecordingBinder skipSources(Class... classesToSkip) {
416      // if a source is specified explicitly, we don't need to skip sources
417      if (source != null) {
418        return this;
419      }
420
421      SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
422      return new RecordingBinder(this, null, newSourceProvider);
423    }
424
425    @Override
426    public PrivateBinder newPrivateBinder() {
427      PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource());
428      RecordingBinder binder = new RecordingBinder(this, privateElements);
429      privateBinders.add(binder);
430      elements.add(privateElements);
431      return binder;
432    }
433
434    @Override
435    public void disableCircularProxies() {
436      elements.add(new DisableCircularProxiesOption(getElementSource()));
437    }
438
439    @Override
440    public void requireExplicitBindings() {
441      elements.add(new RequireExplicitBindingsOption(getElementSource()));
442    }
443
444    @Override
445    public void requireAtInjectOnConstructors() {
446      elements.add(new RequireAtInjectOnConstructorsOption(getElementSource()));
447    }
448
449    @Override
450    public void requireExactBindingAnnotations() {
451      elements.add(new RequireExactBindingAnnotationsOption(getElementSource()));
452    }
453
454    @Override
455    public void scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner) {
456      scanners.add(scanner);
457      elements.add(new ModuleAnnotatedMethodScannerBinding(getElementSource(), scanner));
458    }
459
460    public void expose(Key<?> key) {
461      exposeInternal(key);
462    }
463
464    @Override
465    public AnnotatedElementBuilder expose(Class<?> type) {
466      return exposeInternal(Key.get(type));
467    }
468
469    @Override
470    public AnnotatedElementBuilder expose(TypeLiteral<?> type) {
471      return exposeInternal(Key.get(type));
472    }
473
474    private <T> AnnotatedElementBuilder exposeInternal(Key<T> key) {
475      if (privateElements == null) {
476        addError("Cannot expose %s on a standard binder. "
477            + "Exposed bindings are only applicable to private binders.", key);
478        return new AnnotatedElementBuilder() {
479          @Override
480          public void annotatedWith(Class<? extends Annotation> annotationType) {}
481          @Override
482          public void annotatedWith(Annotation annotation) {}
483        };
484      }
485
486      ExposureBuilder<T> builder =
487          new ExposureBuilder<T>(this, getElementSource(), MoreTypes.canonicalizeKey(key));
488      privateElements.addExposureBuilder(builder);
489      return builder;
490    }
491
492    private ModuleSource getModuleSource(Object module) {
493      StackTraceElement[] partialCallStack;
494      if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) {
495        partialCallStack = getPartialCallStack(new Throwable().getStackTrace());
496      } else {
497        partialCallStack = new StackTraceElement[0];
498      }
499      if (moduleSource == null) {
500        return new ModuleSource(module, partialCallStack);
501      }
502      return moduleSource.createChild(module, partialCallStack);
503    }
504
505    private ElementSource getElementSource() {
506      // Full call stack
507      StackTraceElement[] callStack = null;
508      // The call stack starts from current top module configure and ends at this method caller
509      StackTraceElement[] partialCallStack = new StackTraceElement[0];
510      // The element original source
511      ElementSource originalSource = null;
512      // The element declaring source
513      Object declaringSource = source;
514      if (declaringSource instanceof ElementSource) {
515        originalSource = (ElementSource) declaringSource;
516        declaringSource = originalSource.getDeclaringSource();
517      }
518      IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption();
519      if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
520          (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE
521          && declaringSource == null)) {
522        callStack = new Throwable().getStackTrace();
523      }
524      if (stackTraceOption == IncludeStackTraceOption.COMPLETE) {
525        partialCallStack = getPartialCallStack(callStack);
526      }
527      if (declaringSource == null) {
528        // So 'source' and 'originalSource' are null otherwise declaringSource has some value
529        if (stackTraceOption == IncludeStackTraceOption.COMPLETE ||
530            stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) {
531          // With the above conditions and assignments 'callStack' is non-null
532          declaringSource = sourceProvider.get(callStack);
533        } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF)
534          // As neither 'declaring source' nor 'call stack' is available use 'module source'
535          declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames());
536        }
537      }
538      // Build the binding call stack
539      return new ElementSource(
540          originalSource, declaringSource, moduleSource, partialCallStack);
541    }
542
543    /**
544     * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It
545     * also removes the last two elements in order to make {@link #install(Module)} the last call
546     * in the call stack.
547     */
548    private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) {
549      int toSkip = 0;
550      if (moduleSource != null) {
551        toSkip = moduleSource.getStackTraceSize();
552      }
553      // -1 for skipping 'getModuleSource' and 'getElementSource' calls
554      int chunkSize = callStack.length - toSkip - 1;
555
556      StackTraceElement[] partialCallStack = new StackTraceElement[chunkSize];
557      System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize);
558      return partialCallStack;
559    }
560
561    @Override public String toString() {
562      return "Binder";
563    }
564  }
565}
566