/*
 * 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 static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
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.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.AnnotatedElementBuilder;
import com.google.inject.internal.AbstractBindingBuilder;
import com.google.inject.internal.BindingBuilder;
import com.google.inject.internal.ConstantBindingBuilderImpl;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ExposureBuilder;
import com.google.inject.internal.InternalFlags.IncludeStackTraceOption;
import com.google.inject.internal.MoreTypes;
import com.google.inject.internal.PrivateElementsImpl;
import com.google.inject.internal.ProviderMethodsModule;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.matcher.Matcher;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Exposes elements of a module so they can be inspected, validated or {@link
 * Element#applyTo(Binder) rewritten}.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 * @since 2.0
 */
public final class Elements {

  private static final BindingTargetVisitor<Object, Object> GET_INSTANCE_VISITOR =
      new DefaultBindingTargetVisitor<Object, Object>() {
        @Override
        public Object visit(InstanceBinding<?> binding) {
          return binding.getInstance();
        }

        @Override
        protected Object visitOther(Binding<?> binding) {
          throw new IllegalArgumentException();
        }
      };

  /** Records the elements executed by {@code modules}. */
  public static List<Element> getElements(Module... modules) {
    return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
  }

  /** Records the elements executed by {@code modules}. */
  public static List<Element> getElements(Stage stage, Module... modules) {
    return getElements(stage, Arrays.asList(modules));
  }

  /** Records the elements executed by {@code modules}. */
  public static List<Element> getElements(Iterable<? extends Module> modules) {
    return getElements(Stage.DEVELOPMENT, modules);
  }

  /** Records the elements executed by {@code modules}. */
  public static List<Element> getElements(Stage stage, Iterable<? extends Module> modules) {
    RecordingBinder binder = new RecordingBinder(stage);
    for (Module module : modules) {
      binder.install(module);
    }
    binder.scanForAnnotatedMethods();
    for (RecordingBinder child : binder.privateBinders) {
      child.scanForAnnotatedMethods();
    }
    // Free the memory consumed by the stack trace elements cache
    StackTraceElements.clearCache();
    return Collections.unmodifiableList(binder.elements);
  }

  private static class ElementsAsModule implements Module {
    private final Iterable<? extends Element> elements;

    ElementsAsModule(Iterable<? extends Element> elements) {
      this.elements = elements;
    }

    @Override
    public void configure(Binder binder) {
      for (Element element : elements) {
        element.applyTo(binder);
      }
    }
  }

  /** Returns the module composed of {@code elements}. */
  public static Module getModule(final Iterable<? extends Element> elements) {
    return new ElementsAsModule(elements);
  }

  @SuppressWarnings("unchecked")
  static <T> BindingTargetVisitor<T, T> getInstanceVisitor() {
    return (BindingTargetVisitor<T, T>) GET_INSTANCE_VISITOR;
  }

  private static class ModuleInfo {
    private final Binder binder;
    private final ModuleSource moduleSource;
    private final boolean skipScanning;

    private ModuleInfo(Binder binder, ModuleSource moduleSource, boolean skipScanning) {
      this.binder = binder;
      this.moduleSource = moduleSource;
      this.skipScanning = skipScanning;
    }
  }

  private static class RecordingBinder implements Binder, PrivateBinder {
    private final Stage stage;
    private final Map<Module, ModuleInfo> modules;
    private final List<Element> elements;
    private final Object source;
    /** The current modules stack */
    private ModuleSource moduleSource = null;

    private final SourceProvider sourceProvider;
    private final Set<ModuleAnnotatedMethodScanner> scanners;

    /** The binder where exposed bindings will be created */
    private final RecordingBinder parent;

    private final PrivateElementsImpl privateElements;

    /** All children private binders, so we can scan through them. */
    private final List<RecordingBinder> privateBinders;

    private RecordingBinder(Stage stage) {
      this.stage = stage;
      this.modules = Maps.newLinkedHashMap();
      this.scanners = Sets.newLinkedHashSet();
      this.elements = Lists.newArrayList();
      this.source = null;
      this.sourceProvider =
          SourceProvider.DEFAULT_INSTANCE.plusSkippedClasses(
              Elements.class,
              RecordingBinder.class,
              AbstractModule.class,
              ConstantBindingBuilderImpl.class,
              AbstractBindingBuilder.class,
              BindingBuilder.class);
      this.parent = null;
      this.privateElements = null;
      this.privateBinders = Lists.newArrayList();
    }

    /** Creates a recording binder that's backed by {@code prototype}. */
    private RecordingBinder(
        RecordingBinder prototype, Object source, SourceProvider sourceProvider) {
      checkArgument(source == null ^ sourceProvider == null);

      this.stage = prototype.stage;
      this.modules = prototype.modules;
      this.elements = prototype.elements;
      this.scanners = prototype.scanners;
      this.source = source;
      this.moduleSource = prototype.moduleSource;
      this.sourceProvider = sourceProvider;
      this.parent = prototype.parent;
      this.privateElements = prototype.privateElements;
      this.privateBinders = prototype.privateBinders;
    }

    /** Creates a private recording binder. */
    private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) {
      this.stage = parent.stage;
      this.modules = Maps.newLinkedHashMap();
      this.scanners = Sets.newLinkedHashSet(parent.scanners);
      this.elements = privateElements.getElementsMutable();
      this.source = parent.source;
      this.moduleSource = parent.moduleSource;
      this.sourceProvider = parent.sourceProvider;
      this.parent = parent;
      this.privateElements = privateElements;
      this.privateBinders = parent.privateBinders;
    }

    /*if[AOP]*/
    @Override
    public void bindInterceptor(
        Matcher<? super Class<?>> classMatcher,
        Matcher<? super Method> methodMatcher,
        org.aopalliance.intercept.MethodInterceptor... interceptors) {
      elements.add(
          new InterceptorBinding(getElementSource(), classMatcher, methodMatcher, interceptors));
    }
    /*end[AOP]*/

    @Override
    public void bindScope(Class<? extends Annotation> annotationType, Scope scope) {
      elements.add(new ScopeBinding(getElementSource(), annotationType, scope));
    }

    @Override
    @SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
    public void requestInjection(Object instance) {
      checkNotNull(instance, "instance");
      requestInjection((TypeLiteral<Object>) TypeLiteral.get(instance.getClass()), instance);
    }

    @Override
    public <T> void requestInjection(TypeLiteral<T> type, T instance) {
      checkNotNull(instance, "instance");
      elements.add(
          new InjectionRequest<T>(
              getElementSource(), MoreTypes.canonicalizeForKey(type), instance));
    }

    @Override
    public <T> MembersInjector<T> getMembersInjector(final TypeLiteral<T> typeLiteral) {
      final MembersInjectorLookup<T> element =
          new MembersInjectorLookup<T>(
              getElementSource(), MoreTypes.canonicalizeForKey(typeLiteral));
      elements.add(element);
      return element.getMembersInjector();
    }

    @Override
    public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
      return getMembersInjector(TypeLiteral.get(type));
    }

    @Override
    public void bindListener(Matcher<? super TypeLiteral<?>> typeMatcher, TypeListener listener) {
      elements.add(new TypeListenerBinding(getElementSource(), listener, typeMatcher));
    }

    @Override
    public void bindListener(
        Matcher<? super Binding<?>> bindingMatcher, ProvisionListener... listeners) {
      elements.add(new ProvisionListenerBinding(getElementSource(), bindingMatcher, listeners));
    }

    @Override
    public void requestStaticInjection(Class<?>... types) {
      for (Class<?> type : types) {
        elements.add(new StaticInjectionRequest(getElementSource(), type));
      }
    }

    /**
     * Applies all scanners to the modules we've installed. We skip certain PrivateModules because
     * store them in more than one Modules map and only want to process them through one of the
     * maps. (They're stored in both maps to prevent a module from being installed more than once.)
     */
    void scanForAnnotatedMethods() {
      for (ModuleAnnotatedMethodScanner scanner : scanners) {
        // Note: we must iterate over a copy of the modules because calling install(..)
        // will mutate modules, otherwise causing a ConcurrentModificationException.
        for (Map.Entry<Module, ModuleInfo> entry : Maps.newLinkedHashMap(modules).entrySet()) {
          Module module = entry.getKey();
          ModuleInfo info = entry.getValue();
          if (info.skipScanning) {
            continue;
          }
          moduleSource = entry.getValue().moduleSource;
          try {
            info.binder.install(ProviderMethodsModule.forModule(module, scanner));
          } catch (RuntimeException e) {
            Collection<Message> messages = Errors.getMessagesFromThrowable(e);
            if (!messages.isEmpty()) {
              elements.addAll(messages);
            } else {
              addError(e);
            }
          }
        }
      }
      moduleSource = null;
    }

    @Override
    public void install(Module module) {
      if (!modules.containsKey(module)) {
        RecordingBinder binder = this;
        boolean unwrapModuleSource = false;
        // Update the module source for the new module
        if (module instanceof ProviderMethodsModule) {
          // There are two reason's we'd want to get the module source in a ProviderMethodsModule.
          // ModuleAnnotatedMethodScanner lets users scan their own modules for @Provides-like
          // bindings.  If they install the module at a top-level, then moduleSource can be null.
          // Also, if they pass something other than 'this' to it, we'd have the wrong source.
          Object delegate = ((ProviderMethodsModule) module).getDelegateModule();
          if (moduleSource == null
              || !moduleSource.getModuleClassName().equals(delegate.getClass().getName())) {
            moduleSource = getModuleSource(delegate);
            unwrapModuleSource = true;
          }
        } else {
          moduleSource = getModuleSource(module);
          unwrapModuleSource = true;
        }
        boolean skipScanning = false;
        if (module instanceof PrivateModule) {
          binder = (RecordingBinder) binder.newPrivateBinder();
          // Store the module in the private binder too so we scan for it.
          binder.modules.put(module, new ModuleInfo(binder, moduleSource, false));
          skipScanning = true; // don't scan this module in the parent's module set.
        }
        // Always store this in the parent binder (even if it was a private module)
        // so that we know not to process it again, and so that scanners inherit down.
        modules.put(module, new ModuleInfo(binder, moduleSource, skipScanning));
        try {
          module.configure(binder);
        } catch (RuntimeException e) {
          Collection<Message> messages = Errors.getMessagesFromThrowable(e);
          if (!messages.isEmpty()) {
            elements.addAll(messages);
          } else {
            addError(e);
          }
        }
        binder.install(ProviderMethodsModule.forModule(module));
        // We are done with this module, so undo module source change
        if (unwrapModuleSource) {
          moduleSource = moduleSource.getParent();
        }
      }
    }

    @Override
    public Stage currentStage() {
      return stage;
    }

    @Override
    public void addError(String message, Object... arguments) {
      elements.add(new Message(getElementSource(), Errors.format(message, arguments)));
    }

    @Override
    public void addError(Throwable t) {
      String message = "An exception was caught and reported. Message: " + t.getMessage();
      elements.add(new Message(ImmutableList.of((Object) getElementSource()), message, t));
    }

    @Override
    public void addError(Message message) {
      elements.add(message);
    }

    @Override
    public <T> AnnotatedBindingBuilder<T> bind(Key<T> key) {
      BindingBuilder<T> builder =
          new BindingBuilder<T>(this, elements, getElementSource(), MoreTypes.canonicalizeKey(key));
      return builder;
    }

    @Override
    public <T> AnnotatedBindingBuilder<T> bind(TypeLiteral<T> typeLiteral) {
      return bind(Key.get(typeLiteral));
    }

    @Override
    public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
      return bind(Key.get(type));
    }

    @Override
    public AnnotatedConstantBindingBuilder bindConstant() {
      return new ConstantBindingBuilderImpl<Void>(this, elements, getElementSource());
    }

    @Override
    public <T> Provider<T> getProvider(final Key<T> key) {
      return getProvider(Dependency.get(key));
    }

    @Override
    public <T> Provider<T> getProvider(final Dependency<T> dependency) {
      final ProviderLookup<T> element = new ProviderLookup<>(getElementSource(), dependency);
      elements.add(element);
      return element.getProvider();
    }

    @Override
    public <T> Provider<T> getProvider(Class<T> type) {
      return getProvider(Key.get(type));
    }

    @Override
    public void convertToTypes(
        Matcher<? super TypeLiteral<?>> typeMatcher, TypeConverter converter) {
      elements.add(new TypeConverterBinding(getElementSource(), typeMatcher, converter));
    }

    @Override
    public RecordingBinder withSource(final Object source) {
      return source == this.source ? this : new RecordingBinder(this, source, null);
    }

    @Override
    public RecordingBinder skipSources(Class... classesToSkip) {
      // if a source is specified explicitly, we don't need to skip sources
      if (source != null) {
        return this;
      }

      SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
      return new RecordingBinder(this, null, newSourceProvider);
    }

    @Override
    public PrivateBinder newPrivateBinder() {
      PrivateElementsImpl privateElements = new PrivateElementsImpl(getElementSource());
      RecordingBinder binder = new RecordingBinder(this, privateElements);
      privateBinders.add(binder);
      elements.add(privateElements);
      return binder;
    }

    @Override
    public void disableCircularProxies() {
      elements.add(new DisableCircularProxiesOption(getElementSource()));
    }

    @Override
    public void requireExplicitBindings() {
      elements.add(new RequireExplicitBindingsOption(getElementSource()));
    }

    @Override
    public void requireAtInjectOnConstructors() {
      elements.add(new RequireAtInjectOnConstructorsOption(getElementSource()));
    }

    @Override
    public void requireExactBindingAnnotations() {
      elements.add(new RequireExactBindingAnnotationsOption(getElementSource()));
    }

    @Override
    public void scanModulesForAnnotatedMethods(ModuleAnnotatedMethodScanner scanner) {
      scanners.add(scanner);
      elements.add(new ModuleAnnotatedMethodScannerBinding(getElementSource(), scanner));
    }

    @Override
    public void expose(Key<?> key) {
      exposeInternal(key);
    }

    @Override
    public AnnotatedElementBuilder expose(Class<?> type) {
      return exposeInternal(Key.get(type));
    }

    @Override
    public AnnotatedElementBuilder expose(TypeLiteral<?> type) {
      return exposeInternal(Key.get(type));
    }

    private <T> AnnotatedElementBuilder exposeInternal(Key<T> key) {
      if (privateElements == null) {
        addError(
            "Cannot expose %s on a standard binder. "
                + "Exposed bindings are only applicable to private binders.",
            key);
        return new AnnotatedElementBuilder() {
          @Override
          public void annotatedWith(Class<? extends Annotation> annotationType) {}

          @Override
          public void annotatedWith(Annotation annotation) {}
        };
      }

      ExposureBuilder<T> builder =
          new ExposureBuilder<T>(this, getElementSource(), MoreTypes.canonicalizeKey(key));
      privateElements.addExposureBuilder(builder);
      return builder;
    }

    private ModuleSource getModuleSource(Object module) {
      StackTraceElement[] partialCallStack;
      if (getIncludeStackTraceOption() == IncludeStackTraceOption.COMPLETE) {
        partialCallStack = getPartialCallStack(new Throwable().getStackTrace());
      } else {
        partialCallStack = new StackTraceElement[0];
      }
      if (moduleSource == null) {
        return new ModuleSource(module, partialCallStack);
      }
      return moduleSource.createChild(module, partialCallStack);
    }

    private ElementSource getElementSource() {
      // Full call stack
      StackTraceElement[] callStack = null;
      // The call stack starts from current top module configure and ends at this method caller
      StackTraceElement[] partialCallStack = new StackTraceElement[0];
      // The element original source
      ElementSource originalSource = null;
      // The element declaring source
      Object declaringSource = source;
      if (declaringSource instanceof ElementSource) {
        originalSource = (ElementSource) declaringSource;
        declaringSource = originalSource.getDeclaringSource();
      }
      IncludeStackTraceOption stackTraceOption = getIncludeStackTraceOption();
      if (stackTraceOption == IncludeStackTraceOption.COMPLETE
          || (stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE
              && declaringSource == null)) {
        callStack = new Throwable().getStackTrace();
      }
      if (stackTraceOption == IncludeStackTraceOption.COMPLETE) {
        partialCallStack = getPartialCallStack(callStack);
      }
      if (declaringSource == null) {
        // So 'source' and 'originalSource' are null otherwise declaringSource has some value
        if (stackTraceOption == IncludeStackTraceOption.COMPLETE
            || stackTraceOption == IncludeStackTraceOption.ONLY_FOR_DECLARING_SOURCE) {
          // With the above conditions and assignments 'callStack' is non-null
          declaringSource = sourceProvider.get(callStack);
        } else { // or if (stackTraceOption == IncludeStackTraceOptions.OFF)
          // As neither 'declaring source' nor 'call stack' is available use 'module source'
          declaringSource = sourceProvider.getFromClassNames(moduleSource.getModuleClassNames());
        }
      }
      // Build the binding call stack
      return new ElementSource(originalSource, declaringSource, moduleSource, partialCallStack);
    }

    /**
     * Removes the {@link #moduleSource} call stack from the beginning of current call stack. It
     * also removes the last two elements in order to make {@link #install(Module)} the last call in
     * the call stack.
     */
    private StackTraceElement[] getPartialCallStack(StackTraceElement[] callStack) {
      int toSkip = 0;
      if (moduleSource != null) {
        toSkip = moduleSource.getStackTraceSize();
      }
      // -1 for skipping 'getModuleSource' and 'getElementSource' calls
      int chunkSize = callStack.length - toSkip - 1;

      StackTraceElement[] partialCallStack = new StackTraceElement[chunkSize];
      System.arraycopy(callStack, 1, partialCallStack, 0, chunkSize);
      return partialCallStack;
    }

    @Override
    public String toString() {
      return "Binder";
    }
  }
}


/*
 * Copyright (C) 2006 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.internal;

import com.google.common.collect.Lists;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.sf.cglib.proxy.MethodProxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * Intercepts a method with a stack of interceptors.
 *
 * @author crazybob@google.com (Bob Lee)
 */
final class InterceptorStackCallback implements net.sf.cglib.proxy.MethodInterceptor {
  private static final Set<String> AOP_INTERNAL_CLASSES =
      new HashSet<String>(
          Arrays.asList(
              InterceptorStackCallback.class.getName(),
              InterceptedMethodInvocation.class.getName(),
              MethodProxy.class.getName()));

  final MethodInterceptor[] interceptors;
  final Method method;

  public InterceptorStackCallback(Method method, List<MethodInterceptor> interceptors) {
    this.method = method;
    this.interceptors = interceptors.toArray(new MethodInterceptor[interceptors.size()]);
  }

  @Override
  public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
      throws Throwable {
    return new InterceptedMethodInvocation(proxy, methodProxy, arguments, 0).proceed();
  }

  private class InterceptedMethodInvocation implements MethodInvocation {

    final Object proxy;
    final Object[] arguments;
    final MethodProxy methodProxy;
    final int index;

    public InterceptedMethodInvocation(
        Object proxy, MethodProxy methodProxy, Object[] arguments, int index) {
      this.proxy = proxy;
      this.methodProxy = methodProxy;
      this.arguments = arguments;
      this.index = index;
    }

    @Override
    public Object proceed() throws Throwable {
      try {
        return index == interceptors.length
            ? methodProxy.invokeSuper(proxy, arguments)
            : interceptors[index]
                .invoke(new InterceptedMethodInvocation(proxy, methodProxy, arguments, index + 1));
      } catch (Throwable t) {
        pruneStacktrace(t);
        throw t;
      }
    }

    @Override
    public Method getMethod() {
      return method;
    }

    @Override
    public Object[] getArguments() {
      return arguments;
    }

    @Override
    public Object getThis() {
      return proxy;
    }

    @Override
    public AccessibleObject getStaticPart() {
      return getMethod();
    }
  }

  /**
   * Removes stacktrace elements related to AOP internal mechanics from the throwable's stack trace
   * and any causes it may have.
   */
  private void pruneStacktrace(Throwable throwable) {
    for (Throwable t = throwable; t != null; t = t.getCause()) {
      StackTraceElement[] stackTrace = t.getStackTrace();
      List<StackTraceElement> pruned = Lists.newArrayList();
      for (StackTraceElement element : stackTrace) {
        String className = element.getClassName();
        if (!AOP_INTERNAL_CLASSES.contains(className) && !className.contains("$EnhancerByGuice$")) {
          pruned.add(element);
        }
      }
      t.setStackTrace(pruned.toArray(new StackTraceElement[pruned.size()]));
    }
  }
}


/*
 * Copyright (C) 2011 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.internal;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.spi.ProvisionListener;
import com.google.inject.spi.ProvisionListenerBinding;
import java.util.List;
import java.util.logging.Logger;

/**
 * {@link ProvisionListenerStackCallback} for each key.
 *
 * @author sameb@google.com (Sam Berlin)
 */
final class ProvisionListenerCallbackStore {

  // TODO(sameb): Consider exposing this in the API somehow?  Maybe?
  // Lots of code often want to skip over the internal stuffs.
  private static final ImmutableSet<Key<?>> INTERNAL_BINDINGS =
      ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class));

  private final ImmutableList<ProvisionListenerBinding> listenerBindings;

  private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache =
      CacheBuilder.newBuilder()
          .build(
              new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() {
                @Override
                public ProvisionListenerStackCallback<?> load(KeyBinding key) {
                  return create(key.binding);
                }
              });

  ProvisionListenerCallbackStore(List<ProvisionListenerBinding> listenerBindings) {
    this.listenerBindings = ImmutableList.copyOf(listenerBindings);
  }

  /**
   * Returns a new {@link ProvisionListenerStackCallback} for the key or {@code null} if there are
   * no listeners
   */
  @SuppressWarnings(
      "unchecked") // the ProvisionListenerStackCallback type always agrees with the passed type
  public <T> ProvisionListenerStackCallback<T> get(Binding<T> binding) {
    // Never notify any listeners for internal bindings.
    if (!INTERNAL_BINDINGS.contains(binding.getKey())) {
      ProvisionListenerStackCallback<T> callback =
          (ProvisionListenerStackCallback<T>)
              cache.getUnchecked(new KeyBinding(binding.getKey(), binding));
      return callback.hasListeners() ? callback : null;
    }
    return null;
  }

  /**
   * Purges a key from the cache. Use this only if the type is not actually valid for binding and
   * needs to be purged. (See issue 319 and
   * ImplicitBindingTest#testCircularJitBindingsLeaveNoResidue and
   * #testInstancesRequestingProvidersForThemselvesWithChildInjectors for examples of when this is
   * necessary.)
   *
   * <p>Returns true if the type was stored in the cache, false otherwise.
   */
  boolean remove(Binding<?> type) {
    return cache.asMap().remove(type) != null;
  }

  /**
   * Creates a new {@link ProvisionListenerStackCallback} with the correct listeners for the key.
   */
  private <T> ProvisionListenerStackCallback<T> create(Binding<T> binding) {
    List<ProvisionListener> listeners = null;
    for (ProvisionListenerBinding provisionBinding : listenerBindings) {
      if (provisionBinding.getBindingMatcher().matches(binding)) {
        if (listeners == null) {
          listeners = Lists.newArrayList();
        }
        listeners.addAll(provisionBinding.getListeners());
      }
    }
    if (listeners == null || listeners.isEmpty()) {
      // Optimization: don't bother constructing the callback if there are
      // no listeners.
      return ProvisionListenerStackCallback.emptyListener();
    }
    return new ProvisionListenerStackCallback<T>(binding, listeners);
  }

  /** A struct that holds key & binding but uses just key for equality/hashcode. */
  private static class KeyBinding {
    final Key<?> key;
    final Binding<?> binding;

    KeyBinding(Key<?> key, Binding<?> binding) {
      this.key = key;
      this.binding = binding;
    }

    @Override
    public boolean equals(Object obj) {
      return obj instanceof KeyBinding && key.equals(((KeyBinding) obj).key);
    }

    @Override
    public int hashCode() {
      return key.hashCode();
    }
  }
}


/*
 * Copyright (C) 2011 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.Injector;
import com.google.inject.internal.util.StackTraceElements;
import java.lang.reflect.Member;

/**
 * A combination of a {@link Dependency} and the {@link Binding#getSource() source} where the
 * dependency was bound.
 *
 * @author sameb@google.com (Sam Berlin)
 * @since 4.0
 * @deprecated The only use of this object is for {@link
 *     ProvisionListener.ProvisionInvocation#getDependencyChain()} which is also deprecated. This
 *     object will also be removed in Guice 4.4.
 */
@Deprecated
public final class DependencyAndSource {
  private final Dependency<?> dependency;
  private final Object source;

  public DependencyAndSource(Dependency<?> dependency, Object source) {
    this.dependency = dependency;
    this.source = source;
  }

  /**
   * Returns the Dependency, if one exists. For anything that can be referenced by {@link
   * Injector#getBinding}, a dependency exists. A dependency will not exist (and this will return
   * null) for types initialized with {@link Binder#requestInjection} or {@link
   * Injector#injectMembers(Object)}, nor will it exist for objects injected into Providers bound
   * with LinkedBindingBuilder#toProvider(Provider).
   */
  public Dependency<?> getDependency() {
    return dependency;
  }

  /**
   * Returns a string describing where this dependency was bound. If the binding was just-in-time,
   * there is no valid binding source, so this describes the class in question.
   */
  public String getBindingSource() {
    if (source instanceof Class) {
      return StackTraceElements.forType((Class) source).toString();
    } else if (source instanceof Member) {
      return StackTraceElements.forMember((Member) source).toString();
    } else {
      return source.toString();
    }
  }

  @Override
  public String toString() {
    Dependency<?> dep = getDependency();
    Object source = getBindingSource();
    if (dep != null) {
      return "Dependency: " + dep + ", source: " + source;
    } else {
      return "Source: " + source;
    }
  }
}


/*
 * Copyright (C) 2009 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.internal;

import static com.google.common.base.Preconditions.checkState;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.Message;
import com.google.inject.spi.TypeEncounter;
import java.lang.reflect.Method;
import java.util.List;

/** @author jessewilson@google.com (Jesse Wilson) */
final class EncounterImpl<T> implements TypeEncounter<T> {

  private final Errors errors;
  private final Lookups lookups;
  private List<MembersInjector<? super T>> membersInjectors; // lazy
  private List<InjectionListener<? super T>> injectionListeners; // lazy
  /*if[AOP]*/
  private List<MethodAspect> aspects; // lazy
  /*end[AOP]*/
  private boolean valid = true;

  EncounterImpl(Errors errors, Lookups lookups) {
    this.errors = errors;
    this.lookups = lookups;
  }

  void invalidate() {
    valid = false;
  }

  /*if[AOP]*/
  ImmutableList<MethodAspect> getAspects() {
    return aspects == null ? ImmutableList.<MethodAspect>of() : ImmutableList.copyOf(aspects);
  }

  @Override
  public void bindInterceptor(
      Matcher<? super Method> methodMatcher,
      org.aopalliance.intercept.MethodInterceptor... interceptors) {
    checkState(valid, "Encounters may not be used after hear() returns.");

    // make sure the applicable aspects is mutable
    if (aspects == null) {
      aspects = Lists.newArrayList();
    }

    aspects.add(new MethodAspect(Matchers.any(), methodMatcher, interceptors));
  }
  /*end[AOP]*/

  ImmutableSet<MembersInjector<? super T>> getMembersInjectors() {
    return membersInjectors == null
        ? ImmutableSet.<MembersInjector<? super T>>of()
        : ImmutableSet.copyOf(membersInjectors);
  }

  ImmutableSet<InjectionListener<? super T>> getInjectionListeners() {
    return injectionListeners == null
        ? ImmutableSet.<InjectionListener<? super T>>of()
        : ImmutableSet.copyOf(injectionListeners);
  }

  @Override
  public void register(MembersInjector<? super T> membersInjector) {
    checkState(valid, "Encounters may not be used after hear() returns.");

    if (membersInjectors == null) {
      membersInjectors = Lists.newArrayList();
    }

    membersInjectors.add(membersInjector);
  }

  @Override
  public void register(InjectionListener<? super T> injectionListener) {
    checkState(valid, "Encounters may not be used after hear() returns.");

    if (injectionListeners == null) {
      injectionListeners = Lists.newArrayList();
    }

    injectionListeners.add(injectionListener);
  }

  @Override
  public void addError(String message, Object... arguments) {
    checkState(valid, "Encounters may not be used after hear() returns.");
    errors.addMessage(message, arguments);
  }

  @Override
  public void addError(Throwable t) {
    checkState(valid, "Encounters may not be used after hear() returns.");
    errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
  }

  @Override
  public void addError(Message message) {
    checkState(valid, "Encounters may not be used after hear() returns.");
    errors.addMessage(message);
  }

  @Override
  public <T> Provider<T> getProvider(Key<T> key) {
    checkState(valid, "Encounters may not be used after hear() returns.");
    return lookups.getProvider(key);
  }

  @Override
  public <T> Provider<T> getProvider(Class<T> type) {
    return getProvider(Key.get(type));
  }

  @Override
  public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
    checkState(valid, "Encounters may not be used after hear() returns.");
    return lookups.getMembersInjector(typeLiteral);
  }

  @Override
  public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
    return getMembersInjector(TypeLiteral.get(type));
  }
}


/*
 * Copyright (C) 2017 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.internal;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;

/** Utility methods for {@link Message} objects */
public final class Messages {
  private Messages() {}

  /** Prepends the list of sources to the given {@link Message} */
  static Message mergeSources(List<Object> sources, Message message) {
    List<Object> messageSources = message.getSources();
    // It is possible that the end of getSources() and the beginning of message.getSources() are
    // equivalent, in this case we should drop the repeated source when joining the lists.  The
    // most likely scenario where this would happen is when a scoped binding throws an exception,
    // due to the fact that InternalFactoryToProviderAdapter applies the binding source when
    // merging errors.
    if (!sources.isEmpty()
        && !messageSources.isEmpty()
        && Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) {
      messageSources = messageSources.subList(1, messageSources.size());
    }
    return new Message(
        ImmutableList.builder().addAll(sources).addAll(messageSources).build(),
        message.getMessage(),
        message.getCause());
  }

  /**
   * Calls {@link String#format} after converting the arguments using some standard guice formatting
   * for {@link Key}, {@link Class} and {@link Member} objects.
   */
  public static String format(String messageFormat, Object... arguments) {
    for (int i = 0; i < arguments.length; i++) {
      arguments[i] = convert(arguments[i]);
    }
    return String.format(messageFormat, arguments);
  }

  /** Returns the formatted message for an exception with the specified messages. */
  public static String formatMessages(String heading, Collection<Message> errorMessages) {
    Formatter fmt = new Formatter().format(heading).format(":%n%n");
    int index = 1;
    boolean displayCauses = getOnlyCause(errorMessages) == null;

    Map<Equivalence.Wrapper<Throwable>, Integer> causes = Maps.newHashMap();
    for (Message errorMessage : errorMessages) {
      int thisIdx = index++;
      fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());

      List<Object> dependencies = errorMessage.getSources();
      for (int i = dependencies.size() - 1; i >= 0; i--) {
        Object source = dependencies.get(i);
        formatSource(fmt, source);
      }

      Throwable cause = errorMessage.getCause();
      if (displayCauses && cause != null) {
        Equivalence.Wrapper<Throwable> causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
        if (!causes.containsKey(causeEquivalence)) {
          causes.put(causeEquivalence, thisIdx);
          fmt.format("Caused by: %s", Throwables.getStackTraceAsString(cause));
        } else {
          int causeIdx = causes.get(causeEquivalence);
          fmt.format(
              "Caused by: %s (same stack trace as error #%s)",
              cause.getClass().getName(), causeIdx);
        }
      }

      fmt.format("%n");
    }

    if (errorMessages.size() == 1) {
      fmt.format("1 error");
    } else {
      fmt.format("%s errors", errorMessages.size());
    }

    return fmt.toString();
  }

  /**
   * Creates a new Message without a cause.
   *
   * @param messageFormat Format string
   * @param arguments format string arguments
   */
  public static Message create(String messageFormat, Object... arguments) {
    return create(null, messageFormat, arguments);
  }

  /**
   * Creates a new Message with the given cause.
   *
   * @param cause The exception that caused the error
   * @param messageFormat Format string
   * @param arguments format string arguments
   */
  public static Message create(Throwable cause, String messageFormat, Object... arguments) {
    return create(cause, ImmutableList.of(), messageFormat, arguments);
  }

  /**
   * Creates a new Message with the given cause and a binding source stack.
   *
   * @param cause The exception that caused the error
   * @param sources The binding sources for the source stack
   * @param messageFormat Format string
   * @param arguments format string arguments
   */
  public static Message create(
      Throwable cause, List<Object> sources, String messageFormat, Object... arguments) {
    String message = format(messageFormat, arguments);
    return new Message(sources, message, cause);
  }

  /** Formats an object in a user friendly way. */
  static Object convert(Object o) {
    ElementSource source = null;
    if (o instanceof ElementSource) {
      source = (ElementSource) o;
      o = source.getDeclaringSource();
    }
    return convert(o, source);
  }

  static Object convert(Object o, ElementSource source) {
    for (Converter<?> converter : converters) {
      if (converter.appliesTo(o)) {
        return appendModules(converter.convert(o), source);
      }
    }
    return appendModules(o, source);
  }

  private static Object appendModules(Object source, ElementSource elementSource) {
    String modules = moduleSourceString(elementSource);
    if (modules.length() == 0) {
      return source;
    } else {
      return source + modules;
    }
  }

  private static String moduleSourceString(ElementSource elementSource) {
    // if we only have one module (or don't know what they are), then don't bother
    // reporting it, because the source already is going to report exactly that module.
    if (elementSource == null) {
      return "";
    }
    List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
    // Insert any original element sources w/ module info into the path.
    while (elementSource.getOriginalElementSource() != null) {
      elementSource = elementSource.getOriginalElementSource();
      modules.addAll(0, elementSource.getModuleClassNames());
    }
    if (modules.size() <= 1) {
      return "";
    }

    // Ideally we'd do:
    //    return Joiner.on(" -> ")
    //        .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
    //        .append(")").toString();
    // ... but for some reason we can't find Lists.reverse, so do it the boring way.
    StringBuilder builder = new StringBuilder(" (via modules: ");
    for (int i = modules.size() - 1; i >= 0; i--) {
      builder.append(modules.get(i));
      if (i != 0) {
        builder.append(" -> ");
      }
    }
    builder.append(")");
    return builder.toString();
  }

  static void formatSource(Formatter formatter, Object source) {
    ElementSource elementSource = null;
    if (source instanceof ElementSource) {
      elementSource = (ElementSource) source;
      source = elementSource.getDeclaringSource();
    }
    formatSource(formatter, source, elementSource);
  }

  static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
    String modules = moduleSourceString(elementSource);
    if (source instanceof Dependency) {
      Dependency<?> dependency = (Dependency<?>) source;
      InjectionPoint injectionPoint = dependency.getInjectionPoint();
      if (injectionPoint != null) {
        formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
      } else {
        formatSource(formatter, dependency.getKey(), elementSource);
      }

    } else if (source instanceof InjectionPoint) {
      formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);

    } else if (source instanceof Class) {
      formatter.format("  at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);

    } else if (source instanceof Member) {
      formatter.format("  at %s%s%n", StackTraceElements.forMember((Member) source), modules);

    } else if (source instanceof TypeLiteral) {
      formatter.format("  while locating %s%s%n", source, modules);

    } else if (source instanceof Key) {
      Key<?> key = (Key<?>) source;
      formatter.format("  while locating %s%n", convert(key, elementSource));

    } else if (source instanceof Thread) {
      formatter.format("  in thread %s%n", source);

    } else {
      formatter.format("  at %s%s%n", source, modules);
    }
  }

  private static void formatInjectionPoint(
      Formatter formatter,
      Dependency<?> dependency,
      InjectionPoint injectionPoint,
      ElementSource elementSource) {
    Member member = injectionPoint.getMember();
    Class<? extends Member> memberType = Classes.memberType(member);

    if (memberType == Field.class) {
      dependency = injectionPoint.getDependencies().get(0);
      formatter.format("  while locating %s%n", convert(dependency.getKey(), elementSource));
      formatter.format("    for field at %s%n", StackTraceElements.forMember(member));

    } else if (dependency != null) {
      formatter.format("  while locating %s%n", convert(dependency.getKey(), elementSource));
      formatter.format("    for %s%n", formatParameter(dependency));

    } else {
      formatSource(formatter, injectionPoint.getMember());
    }
  }

  static String formatParameter(Dependency<?> dependency) {
    int ordinal = dependency.getParameterIndex() + 1;
    return String.format(
        "the %s%s parameter of %s",
        ordinal,
        getOrdinalSuffix(ordinal),
        StackTraceElements.forMember(dependency.getInjectionPoint().getMember()));
  }

  /**
   * Maps {@code 1} to the string {@code "1st"} ditto for all non-negative numbers
   *
   * @see
   *     https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers
   */
  private static String getOrdinalSuffix(int ordinal) {
    // negative ordinals don't make sense, we allow zero though because we are programmers
    checkArgument(ordinal >= 0);
    if ((ordinal / 10) % 10 == 1) {
      // all the 'teens' are weird
      return "th";
    } else {
      // could use a lookup table? any better?
      switch (ordinal % 10) {
        case 1:
          return "st";
        case 2:
          return "nd";
        case 3:
          return "rd";
        default:
          return "th";
      }
    }
  }

  private abstract static class Converter<T> {

    final Class<T> type;

    Converter(Class<T> type) {
      this.type = type;
    }

    boolean appliesTo(Object o) {
      return o != null && type.isAssignableFrom(o.getClass());
    }

    String convert(Object o) {
      return toString(type.cast(o));
    }

    abstract String toString(T t);
  }

  @SuppressWarnings({"unchecked", "rawtypes"}) // rawtypes aren't avoidable
  private static final Collection<Converter<?>> converters =
      ImmutableList.of(
          new Converter<Class>(Class.class) {
            @Override
            public String toString(Class c) {
              return c.getName();
            }
          },
          new Converter<Member>(Member.class) {
            @Override
            public String toString(Member member) {
              return Classes.toString(member);
            }
          },
          new Converter<Key>(Key.class) {
            @Override
            public String toString(Key key) {
              if (key.getAnnotationType() != null) {
                return key.getTypeLiteral()
                    + " annotated with "
                    + (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
              } else {
                return key.getTypeLiteral().toString();
              }
            }
          });

  /**
   * Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
   * zero or multiple messages with causes, null is returned.
   */
  public static Throwable getOnlyCause(Collection<Message> messages) {
    Throwable onlyCause = null;
    for (Message message : messages) {
      Throwable messageCause = message.getCause();
      if (messageCause == null) {
        continue;
      }

      if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
        return null;
      }

      onlyCause = messageCause;
    }

    return onlyCause;
  }

  private static final class ThrowableEquivalence extends Equivalence<Throwable> {
    static final ThrowableEquivalence INSTANCE = new ThrowableEquivalence();

    @Override
    protected boolean doEquivalent(Throwable a, Throwable b) {
      return a.getClass().equals(b.getClass())
          && Objects.equal(a.getMessage(), b.getMessage())
          && Arrays.equals(a.getStackTrace(), b.getStackTrace())
          && equivalent(a.getCause(), b.getCause());
    }

    @Override
    protected int doHash(Throwable t) {
      return Objects.hashCode(t.getClass().hashCode(), t.getMessage(), hash(t.getCause()));
    }
  }
}


/*
 * Copyright (C) 2013 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.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.inject.Module;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.internal.util.StackTraceElements.InMemoryStackTraceElement;
import java.util.List;

/**
 * Associated to a {@link Module module}, provides the module class name, the parent module {@link
 * ModuleSource source}, and the call stack that ends just before the module {@link
 * Module#configure(Binder) configure(Binder)} method invocation.
 */
final class ModuleSource {

  /** The class name of module that this {@link ModuleSource} associated to. */
  private final String moduleClassName;

  /** The parent {@link ModuleSource module source}. */
  private final ModuleSource parent;

  /**
   * The chunk of call stack that starts from the parent module {@link Module#configure(Binder)
   * configure(Binder)} call and ends just before the module {@link Module#configure(Binder)
   * configure(Binder)} method invocation. For a module without a parent module the chunk starts
   * from the bottom of call stack. The array is non-empty if stack trace collection is on.
   */
  private final InMemoryStackTraceElement[] partialCallStack;

  /**
   * Creates a new {@link ModuleSource} with a {@literal null} parent.
   *
   * @param module the corresponding module
   * @param partialCallStack the chunk of call stack that starts from the parent module {@link
   *     Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
   *     Module#configure(Binder) configure(Binder)} method invocation
   */
  ModuleSource(Object module, StackTraceElement[] partialCallStack) {
    this(null, module, partialCallStack);
  }

  /**
   * Creates a new {@link ModuleSource} Object.
   *
   * @param parent the parent module {@link ModuleSource source}
   * @param module the corresponding module
   * @param partialCallStack the chunk of call stack that starts from the parent module {@link
   *     Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
   *     Module#configure(Binder) configure(Binder)} method invocation
   */
  private ModuleSource(
      /* @Nullable */ ModuleSource parent, Object module, StackTraceElement[] partialCallStack) {
    Preconditions.checkNotNull(module, "module cannot be null.");
    Preconditions.checkNotNull(partialCallStack, "partialCallStack cannot be null.");
    this.parent = parent;
    this.moduleClassName = module.getClass().getName();
    this.partialCallStack = StackTraceElements.convertToInMemoryStackTraceElement(partialCallStack);
  }

  /**
   * Returns the corresponding module class name.
   *
   * @see Class#getName()
   */
  String getModuleClassName() {
    return moduleClassName;
  }

  /**
   * Returns the chunk of call stack that starts from the parent module {@link
   * Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
   * Module#configure(Binder) configure(Binder)} method invocation. The return array is non-empty
   * only if stack trace collection is on.
   */
  StackTraceElement[] getPartialCallStack() {
    return StackTraceElements.convertToStackTraceElement(partialCallStack);
  }

  /** Returns the size of partial call stack if stack trace collection is on otherwise zero. */
  int getPartialCallStackSize() {
    return partialCallStack.length;
  }

  /**
   * Creates and returns a child {@link ModuleSource} corresponding to the {@link Module module}.
   *
   * @param module the corresponding module
   * @param partialCallStack the chunk of call stack that starts from the parent module {@link
   *     Module#configure(Binder) configure(Binder)} call and ends just before the module {@link
   *     Module#configure(Binder) configure(Binder)} method invocation
   */
  ModuleSource createChild(Object module, StackTraceElement[] partialCallStack) {
    return new ModuleSource(this, module, partialCallStack);
  }

  /** Returns the parent module {@link ModuleSource source}. */
  ModuleSource getParent() {
    return parent;
  }

  /**
   * Returns the class names of modules in this module source. The first element (index 0) is filled
   * by this object {@link #getModuleClassName()}. The second element is filled by the parent's
   * {@link #getModuleClassName()} and so on.
   */
  List<String> getModuleClassNames() {
    ImmutableList.Builder<String> classNames = ImmutableList.builder();
    ModuleSource current = this;
    while (current != null) {
      String className = current.moduleClassName;
      classNames.add(className);
      current = current.parent;
    }
    return classNames.build();
  }

  /**
   * Returns the size of {@link ModuleSource ModuleSources} chain (all parents) that ends at this
   * object.
   */
  int size() {
    if (parent == null) {
      return 1;
    }
    return parent.size() + 1;
  }

  /**
   * Returns the size of call stack that ends just before the module {@link Module#configure(Binder)
   * configure(Binder)} method invocation (see {@link #getStackTrace()}).
   */
  int getStackTraceSize() {
    if (parent == null) {
      return partialCallStack.length;
    }
    return parent.getStackTraceSize() + partialCallStack.length;
  }

  /**
   * Returns the full call stack that ends just before the module {@link Module#configure(Binder)
   * configure(Binder)} method invocation. The return array is non-empty if stack trace collection
   * on.
   */
  StackTraceElement[] getStackTrace() {
    int stackTraceSize = getStackTraceSize();
    StackTraceElement[] callStack = new StackTraceElement[stackTraceSize];
    int cursor = 0;
    ModuleSource current = this;
    while (current != null) {
      StackTraceElement[] chunk =
          StackTraceElements.convertToStackTraceElement(current.partialCallStack);
      int chunkSize = chunk.length;
      System.arraycopy(chunk, 0, callStack, cursor, chunkSize);
      current = current.parent;
      cursor = cursor + chunkSize;
    }
    return callStack;
  }
}


/*
 * Copyright (C) 2010 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.mini;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.inject.Provider;

/**
 * Proof of concept. A tiny injector suitable for tiny applications.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 * @since 3.0
 */
public final class MiniGuice {
  private static final Object UNINITIALIZED = new Object();

  private MiniGuice() {}

  private final Map<Key, Provider<?>> bindings = new HashMap<>();
  private final Queue<RequiredKey> requiredKeys = new ArrayDeque<>();
  private final Set<Key> singletons = new HashSet<>();

  /**
   * Creates an injector defined by {@code modules} and immediately uses it to create an instance of
   * {@code type}. The modules can be of any type, and must contain {@code @Provides} methods.
   *
   *
The following injection features are supported:
   *
   *

   *
Field injection. A class may have any number of field injections, and fields may be of any
   *     visibility. Static fields will be injected each time an instance is injected.
   *
Constructor injection. A class may have a single {@code @Inject}-annotated constructor.
   *     Classes that have fields injected may omit the {@link @Inject} annotation if they have a
   *     public no-arguments constructor.
   *
Injection of {@code @Provides} method parameters.
   *
{@code @Provides} methods annotated {@code @Singleton}.
   *
Constructor-injected classes annotated {@code @Singleton}.
   *
Injection of {@link Provider}s.
   *
Binding annotations on injected parameters and fields.
   *
Guice annotations.
   *
JSR 330 annotations.
   *
Eager loading of singletons.
   *

   *
   *
Note that method injection is not supported.
   */
  public static <T> T inject(Class<T> type, Object... modules) {
    Key key = new Key(type, null);
    MiniGuice miniGuice = new MiniGuice();
    for (Object module : modules) {
      miniGuice.install(module);
    }
    miniGuice.requireKey(key, "root injection");
    miniGuice.addJitBindings();
    miniGuice.addProviderBindings();
    miniGuice.eagerlyLoadSingletons();
    Provider<?> provider = miniGuice.bindings.get(key);
    return type.cast(provider.get());
  }

  private void addProviderBindings() {
    Map<Key, Provider<?>> providerBindings = new HashMap<>();
    for (final Map.Entry<Key, Provider<?>> binding : bindings.entrySet()) {
      Key key = binding.getKey();
      final Provider<?> value = binding.getValue();
      Provider<Provider<?>> providerProvider =
          new Provider<Provider<?>>() {
            @Override
            public Provider<?> get() {
              return value;
            }
          };
      providerBindings.put(
          new Key(new ProviderType(javax.inject.Provider.class, key.type), key.annotation),
          providerProvider);
    }
    bindings.putAll(providerBindings);
  }

  private void requireKey(Key key, Object requiredBy) {
    if (key.type instanceof ParameterizedType
        && (((ParameterizedType) key.type).getRawType() == Provider.class
            || ((ParameterizedType) key.type).getRawType() == javax.inject.Provider.class)) {
      Type type = ((ParameterizedType) key.type).getActualTypeArguments()[0];
      key = new Key(type, key.annotation);
    }

    requiredKeys.add(new RequiredKey(key, requiredBy));
  }

  private void eagerlyLoadSingletons() {
    for (Key key : singletons) {
      Provider<?> provider = bindings.get(key);
      final Object onlyInstance = provider.get();
      bindings.put(
          key,
          new Provider<Object>() {
            @Override
            public Object get() {
              return onlyInstance;
            }
          });
    }
  }

  public void install(Object module) {
    boolean hasProvidesMethods = false;
    for (Class<?> c = module.getClass(); c != Object.class; c = c.getSuperclass()) {
      for (Method method : c.getDeclaredMethods()) {
        if (method.isAnnotationPresent(com.google.inject.Provides.class)) {
          Key key = key(method, method.getGenericReturnType(), method.getAnnotations());
          addProviderMethodBinding(key, module, method);
          hasProvidesMethods = true;
        }
      }
    }
    if (!hasProvidesMethods) {
      throw new IllegalArgumentException("No @Provides methods on " + module);
    }
  }

  private void addProviderMethodBinding(Key key, final Object instance, final Method method) {
    final Key[] parameterKeys =
        parametersToKeys(
            method, method.getGenericParameterTypes(), method.getParameterAnnotations());
    method.setAccessible(true);
    final Provider<Object> unscoped =
        new Provider<Object>() {
          @Override
          public Object get() {
            Object[] parameters = keysToValues(parameterKeys);
            try {
              return method.invoke(instance, parameters);
            } catch (IllegalAccessException e) {
              throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
              throw new RuntimeException(e.getCause());
            }
          }
        };

    boolean singleton = method.isAnnotationPresent(javax.inject.Singleton.class);
    putBinding(key, unscoped, singleton);
  }

  private void addJitBindings() {
    RequiredKey requiredKey;
    while ((requiredKey = requiredKeys.poll()) != null) {
      Key key = requiredKey.key;
      if (bindings.containsKey(key)) {
        continue;
      }
      if (!(key.type instanceof Class) || key.annotation != null) {
        throw new IllegalArgumentException("No binding for " + key);
      }
      addJitBinding(key, requiredKey.requiredBy);
    }
  }

  private void addJitBinding(Key key, Object requiredBy) {
    Class<?> type = (Class<?>) key.type;

    /*
     * Lookup the injectable fields and their corresponding keys.
     */
    final List<Field> injectedFields = new ArrayList<>();
    List<Object> fieldKeysList = new ArrayList<>();
    for (Class<?> c = type; c != Object.class; c = c.getSuperclass()) {
      for (Field field : c.getDeclaredFields()) {
        if (!field.isAnnotationPresent(javax.inject.Inject.class)) {
          continue;
        }
        field.setAccessible(true);
        injectedFields.add(field);
        Key fieldKey = key(field, field.getGenericType(), field.getAnnotations());
        fieldKeysList.add(fieldKey);
        requireKey(fieldKey, field);
      }
    }
    final Key[] fieldKeys = fieldKeysList.toArray(new Key[fieldKeysList.size()]);

    /*
     * Lookup @Inject-annotated constructors. If there's no @Inject-annotated
     * constructor, use a default constructor if the class has other injections.
     */
    Constructor<?> injectedConstructor = null;
    for (Constructor<?> constructor : type.getDeclaredConstructors()) {
      if (!constructor.isAnnotationPresent(javax.inject.Inject.class)) {
        continue;
      }
      if (injectedConstructor != null) {
        throw new IllegalArgumentException("Too many injectable constructors on " + type);
      }
      constructor.setAccessible(true);
      injectedConstructor = constructor;
    }
    if (injectedConstructor == null) {
      if (fieldKeys.length == 0) {
        throw new IllegalArgumentException(
            "No injectable constructor on " + type + " required by " + requiredBy);
      }
      try {
        injectedConstructor = type.getConstructor();
      } catch (NoSuchMethodException e) {
        throw new IllegalArgumentException(
            "No injectable constructor on " + type + " required by " + requiredBy);
      }
    }

    /*
     * Create a provider that invokes the constructor and sets its fields.
     */
    final Constructor<?> constructor = injectedConstructor;
    final Key[] parameterKeys =
        parametersToKeys(
            constructor,
            constructor.getGenericParameterTypes(),
            constructor.getParameterAnnotations());
    final Provider<Object> unscoped =
        new Provider<Object>() {
          @Override
          public Object get() {
            Object[] constructorParameters = keysToValues(parameterKeys);
            try {
              Object result = constructor.newInstance(constructorParameters);
              Object[] fieldValues = keysToValues(fieldKeys);
              for (int i = 0; i < fieldValues.length; i++) {
                injectedFields.get(i).set(result, fieldValues[i]);
              }
              return result;
            } catch (IllegalAccessException e) {
              throw new RuntimeException(e.getCause());
            } catch (InvocationTargetException e) {
              throw new RuntimeException(e.getCause());
            } catch (InstantiationException e) {
              throw new RuntimeException(e);
            }
          }
        };

    boolean singleton = type.isAnnotationPresent(javax.inject.Singleton.class);
    putBinding(new Key(type, null), unscoped, singleton);
  }

  private void putBinding(Key key, Provider<Object> provider, boolean singleton) {
    if (singleton) {
      singletons.add(key);
      final Provider<Object> unscoped = provider;
      provider =
          new Provider<Object>() {
            private Object onlyInstance = UNINITIALIZED;

            @Override
            public Object get() {
              if (onlyInstance == UNINITIALIZED) {
                onlyInstance = unscoped.get();
              }
              return onlyInstance;
            }
          };
    }

    if (bindings.put(key, provider) != null) {
      throw new IllegalArgumentException("Duplicate binding " + key);
    }
  }

  private Object[] keysToValues(Key[] parameterKeys) {
    Object[] parameters = new Object[parameterKeys.length];
    for (int i = 0; i < parameterKeys.length; i++) {
      parameters[i] = bindings.get(parameterKeys[i]).get();
    }
    return parameters;
  }

  private Key[] parametersToKeys(Member member, Type[] types, Annotation[][] annotations) {
    final Key[] parameterKeys = new Key[types.length];
    for (int i = 0; i < parameterKeys.length; i++) {
      String name = member + " parameter " + i;
      parameterKeys[i] = key(name, types[i], annotations[i]);
      requireKey(parameterKeys[i], name);
    }
    return parameterKeys;
  }

  public Key key(Object subject, Type type, Annotation[] annotations) {
    Annotation bindingAnnotation = null;
    for (Annotation a : annotations) {
      if (!a.annotationType().isAnnotationPresent(javax.inject.Qualifier.class)) {
        continue;
      }
      if (bindingAnnotation != null) {
        throw new IllegalArgumentException("Too many binding annotations on " + subject);
      }
      bindingAnnotation = a;
    }
    return new Key(type, bindingAnnotation);
  }

  private static boolean equal(Object a, Object b) {
    return a == null ? b == null : a.equals(b);
  }

  private static final class Key {
    final Type type;
    final Annotation annotation;

    Key(Type type, Annotation annotation) {
      this.type = type;
      this.annotation = annotation;
    }

    @Override
    public boolean equals(Object o) {
      return o instanceof Key
          && ((Key) o).type.equals(type)
          && equal(annotation, ((Key) o).annotation);
    }

    @Override
    public int hashCode() {
      int result = type.hashCode();
      if (annotation != null) {
        result += (37 * annotation.hashCode());
      }
      return result;
    }

    @Override
    public String toString() {
      return "key[type=" + type + ",annotation=" + annotation + "]";
    }
  }

  private static class RequiredKey {
    private final Key key;
    private final Object requiredBy;

    private RequiredKey(Key key, Object requiredBy) {
      this.key = key;
      this.requiredBy = requiredBy;
    }
  }

  private static final class ProviderType implements ParameterizedType {
    private final Class<?> rawType;
    private final Type typeArgument;

    public ProviderType(Class<?> rawType, Type typeArgument) {
      this.rawType = rawType;
      this.typeArgument = typeArgument;
    }

    @Override
    public Type getRawType() {
      return rawType;
    }

    @Override
    public Type[] getActualTypeArguments() {
      return new Type[] {typeArgument};
    }

    @Override
    public Type getOwnerType() {
      return null;
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof ParameterizedType) {
        ParameterizedType that = (ParameterizedType) o;
        return Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments())
            && that.getRawType() == rawType;
      }
      return false;
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(getActualTypeArguments()) ^ rawType.hashCode();
    }
  }
}


/*
 * Copyright (C) 2006 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.internal.util;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.List;

/**
 * Provides access to the calling line of code.
 *
 * @author crazybob@google.com (Bob Lee)
 */
public final class SourceProvider {

  /** Indicates that the source is unknown. */
  public static final Object UNKNOWN_SOURCE = "[unknown source]";

  private final SourceProvider parent;
  private final ImmutableSet<String> classNamesToSkip;

  public static final SourceProvider DEFAULT_INSTANCE =
      new SourceProvider(ImmutableSet.of(SourceProvider.class.getName()));

  private SourceProvider(Iterable<String> classesToSkip) {
    this(null, classesToSkip);
  }

  private SourceProvider(SourceProvider parent, Iterable<String> classesToSkip) {
    this.parent = parent;

    ImmutableSet.Builder<String> classNamesToSkipBuilder = ImmutableSet.builder();
    for (String classToSkip : classesToSkip) {
      if (parent == null || !parent.shouldBeSkipped(classToSkip)) {
        classNamesToSkipBuilder.add(classToSkip);
      }
    }
    this.classNamesToSkip = classNamesToSkipBuilder.build();
  }

  /** Returns a new instance that also skips {@code moreClassesToSkip}. */
  public SourceProvider plusSkippedClasses(Class... moreClassesToSkip) {
    return new SourceProvider(this, asStrings(moreClassesToSkip));
  }

  /** Returns true if the className should be skipped. */
  private boolean shouldBeSkipped(String className) {
    return (parent != null && parent.shouldBeSkipped(className))
        || classNamesToSkip.contains(className);
  }

  /** Returns the class names as Strings */
  private static List<String> asStrings(Class... classes) {
    List<String> strings = Lists.newArrayList();
    for (Class c : classes) {
      strings.add(c.getName());
    }
    return strings;
  }

  /**
   * Returns the calling line of code. The selected line is the nearest to the top of the stack that
   * is not skipped.
   */
  public StackTraceElement get(StackTraceElement[] stackTraceElements) {
    Preconditions.checkNotNull(stackTraceElements, "The stack trace elements cannot be null.");
    for (final StackTraceElement element : stackTraceElements) {
      String className = element.getClassName();

      if (!shouldBeSkipped(className)) {
        return element;
      }
    }
    throw new AssertionError();
  }

  /** Returns the non-skipped module class name. */
  public Object getFromClassNames(List<String> moduleClassNames) {
    Preconditions.checkNotNull(moduleClassNames, "The list of module class names cannot be null.");
    for (final String moduleClassName : moduleClassNames) {
      if (!shouldBeSkipped(moduleClassName)) {
        return new StackTraceElement(moduleClassName, "configure", null, -1);
      }
    }
    return UNKNOWN_SOURCE;
  }
}


/*
 * Copyright (C) 2006 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.tools.jmx;

import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import java.lang.annotation.Annotation;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/**
 * Provides a JMX interface to Guice.
 *
 * @author crazybob@google.com (Bob Lee)
 */
public class Manager {

  /**
   * Registers all the bindings of an Injector with the platform MBean server. Consider using the
   * name of your root {@link Module} class as the domain.
   */
  public static void manage(String domain, Injector injector) {
    manage(ManagementFactory.getPlatformMBeanServer(), domain, injector);
  }

  /**
   * Registers all the bindings of an Injector with the given MBean server. Consider using the name
   * of your root {@link Module} class as the domain.
   */
  public static void manage(MBeanServer server, String domain, Injector injector) {
    // Register each binding independently.
    for (Binding<?> binding : injector.getBindings().values()) {
      // Construct the name manually so we can ensure proper ordering of the
      // key/value pairs.
      StringBuilder name = new StringBuilder();
      name.append(domain).append(":");
      Key<?> key = binding.getKey();
      name.append("type=").append(quote(key.getTypeLiteral().toString()));
      Annotation annotation = key.getAnnotation();
      if (annotation != null) {
        name.append(",annotation=").append(quote(annotation.toString()));
      } else {
        Class<? extends Annotation> annotationType = key.getAnnotationType();
        if (annotationType != null) {
          name.append(",annotation=").append(quote("@" + annotationType.getName()));
        }
      }

      try {
        server.registerMBean(new ManagedBinding(binding), new ObjectName(name.toString()));
      } catch (MalformedObjectNameException e) {
        throw new RuntimeException("Bad object name: " + name, e);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  static String quote(String value) {
    // JMX seems to have a comma bug.
    return ObjectName.quote(value).replace(',', ';');
  }

  /** Run with no arguments for usage instructions. */
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println(
          "Usage: java -Dcom.sun.management.jmxremote "
              + Manager.class.getName()
              + " [module class name]");
      System.err.println("Then run 'jconsole' to connect.");
      System.exit(1);
    }

    Module module = (Module) Class.forName(args[0]).newInstance();
    Injector injector = Guice.createInjector(module);

    manage(args[0], injector);

    System.out.println("Press Ctrl+C to exit...");

    // Sleep forever.
    Thread.sleep(Long.MAX_VALUE);
  }
}


/*
 * Copyright (C) 2006 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.internal.util;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Creates stack trace elements for members.
 *
 * @author crazybob@google.com (Bob Lee)
 */
public class StackTraceElements {

  private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
  private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE =
      new InMemoryStackTraceElement[0];

  /*if[AOP]*/
  static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache =
      CacheBuilder.newBuilder()
          .weakKeys()
          .softValues()
          .build(
              new CacheLoader<Class<?>, LineNumbers>() {
                @Override
                public LineNumbers load(Class<?> key) {
                  try {
                    return new LineNumbers(key);
                  } catch (IOException e) {
                    throw new RuntimeException(e);
                  }
                }
              });
  /*end[AOP]*/

  private static final ConcurrentMap<InMemoryStackTraceElement, InMemoryStackTraceElement>
      elementCache = new ConcurrentHashMap<>();
  private static final ConcurrentMap<String, String> stringCache = new ConcurrentHashMap<>();

  private static final String UNKNOWN_SOURCE = "Unknown Source";

  public static Object forMember(Member member) {
    if (member == null) {
      return SourceProvider.UNKNOWN_SOURCE;
    }

    Class declaringClass = member.getDeclaringClass();

    /*if[AOP]*/
    LineNumbers lineNumbers = lineNumbersCache.getUnchecked(declaringClass);
    String fileName = lineNumbers.getSource();
    Integer lineNumberOrNull = lineNumbers.getLineNumber(member);
    int lineNumber = lineNumberOrNull == null ? lineNumbers.getFirstLine() : lineNumberOrNull;
    /*end[AOP]*/
    /*if[NO_AOP]
    String fileName = null;
    int lineNumber = -1;
    end[NO_AOP]*/

    Class<? extends Member> memberType = Classes.memberType(member);
    String memberName = memberType == Constructor.class ? "<init>" : member.getName();
    return new StackTraceElement(declaringClass.getName(), memberName, fileName, lineNumber);
  }

  public static Object forType(Class<?> implementation) {
    /*if[AOP]*/
    LineNumbers lineNumbers = lineNumbersCache.getUnchecked(implementation);
    int lineNumber = lineNumbers.getFirstLine();
    String fileName = lineNumbers.getSource();
    /*end[AOP]*/
    /*if[NO_AOP]
    String fileName = null;
    int lineNumber = -1;
    end[NO_AOP]*/

    return new StackTraceElement(implementation.getName(), "class", fileName, lineNumber);
  }

  /** Clears the internal cache for {@link StackTraceElement StackTraceElements}. */
  public static void clearCache() {
    elementCache.clear();
    stringCache.clear();
  }

  /** Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}. */
  public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement(
      StackTraceElement[] stackTraceElements) {
    if (stackTraceElements.length == 0) {
      return EMPTY_INMEMORY_STACK_TRACE;
    }
    InMemoryStackTraceElement[] inMemoryStackTraceElements =
        new InMemoryStackTraceElement[stackTraceElements.length];
    for (int i = 0; i < stackTraceElements.length; i++) {
      inMemoryStackTraceElements[i] =
          weakIntern(new InMemoryStackTraceElement(stackTraceElements[i]));
    }
    return inMemoryStackTraceElements;
  }

  /**
   * Decodes in-memory stack trace elements to regular {@link StackTraceElement StackTraceElements}.
   */
  public static StackTraceElement[] convertToStackTraceElement(
      InMemoryStackTraceElement[] inMemoryStackTraceElements) {
    if (inMemoryStackTraceElements.length == 0) {
      return EMPTY_STACK_TRACE;
    }
    StackTraceElement[] stackTraceElements =
        new StackTraceElement[inMemoryStackTraceElements.length];
    for (int i = 0; i < inMemoryStackTraceElements.length; i++) {
      String declaringClass = inMemoryStackTraceElements[i].getClassName();
      String methodName = inMemoryStackTraceElements[i].getMethodName();
      int lineNumber = inMemoryStackTraceElements[i].getLineNumber();
      stackTraceElements[i] =
          new StackTraceElement(declaringClass, methodName, UNKNOWN_SOURCE, lineNumber);
    }
    return stackTraceElements;
  }

  private static InMemoryStackTraceElement weakIntern(
      InMemoryStackTraceElement inMemoryStackTraceElement) {
    InMemoryStackTraceElement cached = elementCache.get(inMemoryStackTraceElement);
    if (cached != null) {
      return cached;
    }
    inMemoryStackTraceElement =
        new InMemoryStackTraceElement(
            weakIntern(inMemoryStackTraceElement.getClassName()),
            weakIntern(inMemoryStackTraceElement.getMethodName()),
            inMemoryStackTraceElement.getLineNumber());
    elementCache.put(inMemoryStackTraceElement, inMemoryStackTraceElement);
    return inMemoryStackTraceElement;
  }

  private static String weakIntern(String s) {
    String cached = stringCache.get(s);
    if (cached != null) {
      return cached;
    }
    stringCache.put(s, s);
    return s;
  }

  /** In-Memory version of {@link StackTraceElement} that does not store the file name. */
  public static class InMemoryStackTraceElement {
    private String declaringClass;
    private String methodName;
    private int lineNumber;

    InMemoryStackTraceElement(StackTraceElement ste) {
      this(ste.getClassName(), ste.getMethodName(), ste.getLineNumber());
    }

    InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber) {
      this.declaringClass = declaringClass;
      this.methodName = methodName;
      this.lineNumber = lineNumber;
    }

    String getClassName() {
      return declaringClass;
    }

    String getMethodName() {
      return methodName;
    }

    int getLineNumber() {
      return lineNumber;
    }

    @Override
    public boolean equals(Object obj) {
      if (obj == this) {
        return true;
      }
      if (!(obj instanceof InMemoryStackTraceElement)) {
        return false;
      }
      InMemoryStackTraceElement e = (InMemoryStackTraceElement) obj;
      return e.declaringClass.equals(declaringClass)
          && e.lineNumber == lineNumber
          && methodName.equals(e.methodName);
    }

    @Override
    public int hashCode() {
      int result = 31 * declaringClass.hashCode() + methodName.hashCode();
      result = 31 * result + lineNumber;
      return result;
    }

    @Override
    public String toString() {
      return declaringClass + "." + methodName + "(" + lineNumber + ")";
    }
  }
}


/*
 * Copyright (C) 2006 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.internal;

import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.spi.Dependency;
import java.util.IdentityHashMap;
import java.util.Map;

/**
 * Internal context. Used to coordinate injections and support circular dependencies.
 *
 * @author crazybob@google.com (Bob Lee)
 */
final class InternalContext implements AutoCloseable {

  private final InjectorOptions options;

  private final Map<Object, ConstructionContext<?>> constructionContexts =
      new IdentityHashMap<Object, ConstructionContext<?>>();

  /** Keeps track of the type that is currently being requested for injection. */
  private Dependency<?> dependency;

  /**
   * Keeps track of the hierarchy of types needed during injection.
   *
   *
This is a pairwise combination of dependencies and sources, with dependencies or keys on
   * even indices, and sources on odd indices. This structure is to avoid the memory overhead of
   * DependencyAndSource objects, which can add to several tens of megabytes in large applications.
   */
  private Object[] dependencyStack = new Object[16];

  private int dependencyStackSize = 0;

  /**
   * The number of times {@link #enter()} has been called + 1 for initial construction. This value
   * is decremented when {@link #exit()} is called.
   */
  private int enterCount;

  /**
   * A single element array to clear when the {@link #enterCount} hits {@code 0}.
   *
   *
This is the value stored in the {@code InjectorImpl.localContext} thread local.
   */
  private final Object[] toClear;

  InternalContext(InjectorOptions options, Object[] toClear) {
    this.options = options;
    this.toClear = toClear;
    this.enterCount = 1;
  }

  /** Should only be called by InjectorImpl.enterContext(). */
  void enter() {
    enterCount++;
  }

  /** Should be called any any method that received an instance via InjectorImpl.enterContext(). */
  @Override
  public void close() {
    int newCount = --enterCount;
    if (newCount < 0) {
      throw new IllegalStateException("Called close() too many times");
    }
    if (newCount == 0) {
      toClear[0] = null;
    }
  }

  InjectorOptions getInjectorOptions() {
    return options;
  }

  @SuppressWarnings("unchecked")
  <T> ConstructionContext<T> getConstructionContext(Object key) {
    ConstructionContext<T> constructionContext =
        (ConstructionContext<T>) constructionContexts.get(key);
    if (constructionContext == null) {
      constructionContext = new ConstructionContext<>();
      constructionContexts.put(key, constructionContext);
    }
    return constructionContext;
  }

  Dependency<?> getDependency() {
    return dependency;
  }

  /** Sets the new current dependency & adds it to the state. */
  Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
    Dependency<?> previous = this.dependency;
    this.dependency = dependency;
    doPushState(dependency, source);
    return previous;
  }

  /** Pops the current state & sets the new dependency. */
  void popStateAndSetDependency(Dependency<?> newDependency) {
    popState();
    this.dependency = newDependency;
  }

  /** Adds to the state without setting the dependency. */
  void pushState(com.google.inject.Key<?> key, Object source) {
    doPushState(key, source);
  }

  private void doPushState(Object dependencyOrKey, Object source) {
    int localSize = dependencyStackSize;
    Object[] localStack = dependencyStack;
    if (localStack.length < localSize + 2) {
      localStack = dependencyStack =
        java.util.Arrays.copyOf(localStack, (localStack.length * 3) / 2 + 2);
    }
    localStack[localSize++] = dependencyOrKey;
    localStack[localSize++] = source;
    dependencyStackSize = localSize;
  }

  /** Pops from the state without setting a dependency. */
  void popState() {
    // N.B. we don't null out the array entries.  It isn't necessary since all the objects in the
    // array (Key, Dependency, or Binding source objects) are all tied to the lifetime of the
    // injector, which is greater than the lifetime of this object.  So removing them from the array
    // doesn't matter.
    dependencyStackSize -= 2;
  }

  /** Returns the current dependency chain (all the state stored in the dependencyStack). */
  java.util.List<com.google.inject.spi.DependencyAndSource> getDependencyChain() {
    com.google.common.collect.ImmutableList.Builder<com.google.inject.spi.DependencyAndSource>
        builder = com.google.common.collect.ImmutableList.builder();
    for (int i = 0; i < dependencyStackSize; i += 2) {
      Object evenEntry = dependencyStack[i];
      Dependency<?> dependency;
      if (evenEntry instanceof com.google.inject.Key) {
        dependency = Dependency.get((com.google.inject.Key<?>) evenEntry);
      } else {
        dependency = (Dependency<?>) evenEntry;
      }
      builder.add(new com.google.inject.spi.DependencyAndSource(dependency, dependencyStack[i + 1]));
    }
    return builder.build();
  }

}


/*
 * 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.internal;

/** @author jessewilson@google.com (Jesse Wilson) */
final class Initializables {

  /** Returns an initializable for an instance that requires no initialization. */
  static <T> Initializable<T> of(final T instance) {
    return new Initializable<T>() {
      @Override
      public T get() {
        return instance;
      }

      @Override
      public String toString() {
        return String.valueOf(instance);
      }
    };
  }
}