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

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
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.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.BytecodeGen;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.internal.util.Classes;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The newer implementation of factory provider. This implementation uses a child injector to create
 * values.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 * @author dtm@google.com (Daniel Martin)
 * @author schmitt@google.com (Peter Schmitt)
 * @author sameb@google.com (Sam Berlin)
 */
final class FactoryProvider2<F>
    implements InvocationHandler,
        ProviderWithExtensionVisitor<F>,
        HasDependencies,
        AssistedInjectBinding<F> {

  /** A constant annotation to denote the return value, instead of creating a new one each time. */
  static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();

  // use the logger under a well-known name, not FactoryProvider2
  static final Logger logger = Logger.getLogger(AssistedInject.class.getName());

  /** if a factory method parameter isn't annotated, it gets this annotation. */
  static final Assisted DEFAULT_ANNOTATION =
      new Assisted() {
        @Override
        public String value() {
          return "";
        }

        @Override
        public Class<? extends Annotation> annotationType() {
          return Assisted.class;
        }

        @Override
        public boolean equals(Object o) {
          return o instanceof Assisted && ((Assisted) o).value().isEmpty();
        }

        @Override
        public int hashCode() {
          return 127 * "value".hashCode() ^ "".hashCode();
        }

        @Override
        public String toString() {
          return "@"
              + Assisted.class.getName()
              + "(value="
              + Annotations.memberValueString("")
              + ")";
        }
      };

  /** All the data necessary to perform an assisted inject. */
  private static class AssistData implements AssistedMethod {
    /** the constructor the implementation is constructed with. */
    final Constructor<?> constructor;
    /** the return type in the factory method that the constructor is bound to. */
    final Key<?> returnType;
    /** the parameters in the factory method associated with this data. */
    final ImmutableList<Key<?>> paramTypes;
    /** the type of the implementation constructed */
    final TypeLiteral<?> implementationType;

    /** All non-assisted dependencies required by this method. */
    final Set<Dependency<?>> dependencies;
    /** The factory method associated with this data */
    final Method factoryMethod;

    /** true if {@link #isValidForOptimizedAssistedInject} returned true. */
    final boolean optimized;
    /** the list of optimized providers, empty if not optimized. */
    final List<ThreadLocalProvider> providers;
    /** used to perform optimized factory creations. */
    volatile Binding<?> cachedBinding; // TODO: volatile necessary?

    AssistData(
        Constructor<?> constructor,
        Key<?> returnType,
        ImmutableList<Key<?>> paramTypes,
        TypeLiteral<?> implementationType,
        Method factoryMethod,
        Set<Dependency<?>> dependencies,
        boolean optimized,
        List<ThreadLocalProvider> providers) {
      this.constructor = constructor;
      this.returnType = returnType;
      this.paramTypes = paramTypes;
      this.implementationType = implementationType;
      this.factoryMethod = factoryMethod;
      this.dependencies = dependencies;
      this.optimized = optimized;
      this.providers = providers;
    }

    @Override
    public String toString() {
      return MoreObjects.toStringHelper(getClass())
          .add("ctor", constructor)
          .add("return type", returnType)
          .add("param type", paramTypes)
          .add("implementation type", implementationType)
          .add("dependencies", dependencies)
          .add("factory method", factoryMethod)
          .add("optimized", optimized)
          .add("providers", providers)
          .add("cached binding", cachedBinding)
          .toString();
    }

    @Override
    public Set<Dependency<?>> getDependencies() {
      return dependencies;
    }

    @Override
    public Method getFactoryMethod() {
      return factoryMethod;
    }

    @Override
    public Constructor<?> getImplementationConstructor() {
      return constructor;
    }

    @Override
    public TypeLiteral<?> getImplementationType() {
      return implementationType;
    }
  }

  /** Mapping from method to the data about how the method will be assisted. */
  private final ImmutableMap<Method, AssistData> assistDataByMethod;

  /** Mapping from method to method handle, for generated default methods. */
  private final ImmutableMap<Method, MethodHandle> methodHandleByMethod;

  /** the hosting injector, or null if we haven't been initialized yet */
  private Injector injector;

  /** the factory interface, implemented and provided */
  private final F factory;

  /** The key that this is bound to. */
  private final Key<F> factoryKey;

  /** The binding collector, for equality/hashing purposes. */
  private final BindingCollector collector;

  /**
   * @param factoryKey a key for a Java interface that defines one or more create methods.
   * @param collector binding configuration that maps method return types to implementation types.
   */
  FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
    this.factoryKey = factoryKey;
    this.collector = collector;

    TypeLiteral<F> factoryType = factoryKey.getTypeLiteral();
    Errors errors = new Errors();

    @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
    Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();

    try {
      if (!factoryRawType.isInterface()) {
        throw errors.addMessage("%s must be an interface.", factoryRawType).toException();
      }

      Multimap<String, Method> defaultMethods = HashMultimap.create();
      Multimap<String, Method> otherMethods = HashMultimap.create();
      ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
      // TODO: also grab methods from superinterfaces
      for (Method method : factoryRawType.getMethods()) {
        // Skip static methods
        if (Modifier.isStatic(method.getModifiers())) {
          continue;
        }

        // Skip default methods that java8 may have created.
        if (isDefault(method) && (method.isBridge() || method.isSynthetic())) {
          // Even synthetic default methods need the return type validation...
          // unavoidable consequence of javac8. :-(
          validateFactoryReturnType(errors, method.getReturnType(), factoryRawType);
          defaultMethods.put(method.getName(), method);
          continue;
        }
        otherMethods.put(method.getName(), method);

        TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
        Key<?> returnType;
        try {
          returnType =
              Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
        } catch (ConfigurationException ce) {
          // If this was an error due to returnTypeLiteral not being specified, rephrase
          // it as our factory not being specified, so it makes more sense to users.
          if (isTypeNotSpecified(returnTypeLiteral, ce)) {
            throw errors.keyNotFullySpecified(TypeLiteral.get(factoryRawType)).toException();
          } else {
            throw ce;
          }
        }
        validateFactoryReturnType(errors, returnType.getTypeLiteral().getRawType(), factoryRawType);
        List<TypeLiteral<?>> params = factoryType.getParameterTypes(method);
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        int p = 0;
        List<Key<?>> keys = Lists.newArrayList();
        for (TypeLiteral<?> param : params) {
          Key<?> paramKey = Annotations.getKey(param, method, paramAnnotations[p++], errors);
          Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
          if (underlylingType.equals(Provider.class)
              || underlylingType.equals(javax.inject.Provider.class)) {
            errors.addMessage(
                "A Provider may not be a type in a factory method of an AssistedInject."
                    + "\n  Offending instance is parameter [%s] with key [%s] on method [%s]",
                p, paramKey, method);
          }
          keys.add(assistKey(method, paramKey, errors));
        }
        ImmutableList<Key<?>> immutableParamList = ImmutableList.copyOf(keys);

        // try to match up the method to the constructor
        TypeLiteral<?> implementation = collector.getBindings().get(returnType);
        if (implementation == null) {
          implementation = returnType.getTypeLiteral();
        }
        Class<? extends Annotation> scope =
            Annotations.findScopeAnnotation(errors, implementation.getRawType());
        if (scope != null) {
          errors.addMessage(
              "Found scope annotation [%s] on implementation class "
                  + "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
                  + " remove the scope annotation.",
              scope, implementation.getRawType(), factoryType);
        }

        InjectionPoint ctorInjectionPoint;
        try {
          ctorInjectionPoint =
              findMatchingConstructorInjectionPoint(
                  method, returnType, implementation, immutableParamList);
        } catch (ErrorsException ee) {
          errors.merge(ee.getErrors());
          continue;
        }

        Constructor<?> constructor = (Constructor<?>) ctorInjectionPoint.getMember();
        List<ThreadLocalProvider> providers = Collections.emptyList();
        Set<Dependency<?>> deps = getDependencies(ctorInjectionPoint, implementation);
        boolean optimized = false;
        // Now go through all dependencies of the implementation and see if it is OK to
        // use an optimized form of assistedinject2.  The optimized form requires that
        // all injections directly inject the object itself (and not a Provider of the object,
        // or an Injector), because it caches a single child injector and mutates the Provider
        // of the arguments in a ThreadLocal.
        if (isValidForOptimizedAssistedInject(deps, implementation.getRawType(), factoryType)) {
          ImmutableList.Builder<ThreadLocalProvider> providerListBuilder = ImmutableList.builder();
          for (int i = 0; i < params.size(); i++) {
            providerListBuilder.add(new ThreadLocalProvider());
          }
          providers = providerListBuilder.build();
          optimized = true;
        }

        AssistData data =
            new AssistData(
                constructor,
                returnType,
                immutableParamList,
                implementation,
                method,
                removeAssistedDeps(deps),
                optimized,
                providers);
        assistDataBuilder.put(method, data);
      }

      factory =
          factoryRawType.cast(
              Proxy.newProxyInstance(
                  BytecodeGen.getClassLoader(factoryRawType),
                  new Class<?>[] {factoryRawType},
                  this));

      // Now go back through default methods. Try to use MethodHandles to make things
      // work.  If that doesn't work, fallback to trying to find compatible method
      // signatures.
      Map<Method, AssistData> dataSoFar = assistDataBuilder.build();
      ImmutableMap.Builder<Method, MethodHandle> methodHandleBuilder = ImmutableMap.builder();
      for (Map.Entry<String, Method> entry : defaultMethods.entries()) {
        Method defaultMethod = entry.getValue();
        MethodHandle handle = createMethodHandle(defaultMethod, factory);
        if (handle != null) {
          methodHandleBuilder.put(defaultMethod, handle);
        } else {
          boolean foundMatch = false;
          for (Method otherMethod : otherMethods.get(defaultMethod.getName())) {
            if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) {
              if (foundMatch) {
                errors.addMessage(
                    "Generated default method %s with parameters %s is"
                        + " signature-compatible with more than one non-default method."
                        + " Unable to create factory. As a workaround, remove the override"
                        + " so javac stops generating a default method.",
                    defaultMethod, Arrays.asList(defaultMethod.getParameterTypes()));
              } else {
                assistDataBuilder.put(defaultMethod, dataSoFar.get(otherMethod));
                foundMatch = true;
              }
            }
          }
          if (!foundMatch) {
            throw new IllegalStateException("Can't find method compatible with: " + defaultMethod);
          }
        }
      }

      // If we generated any errors (from finding matching constructors, for instance), throw an
      // exception.
      if (errors.hasErrors()) {
        throw errors.toException();
      }

      assistDataByMethod = assistDataBuilder.build();
      methodHandleByMethod = methodHandleBuilder.build();
    } catch (ErrorsException e) {
      throw new ConfigurationException(e.getErrors().getMessages());
    }
  }

  static boolean isDefault(Method method) {
    // Per the javadoc, default methods are non-abstract, public, non-static.
    // They're also in interfaces, but we can guarantee that already since we only act
    // on interfaces.
    return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC))
        == Modifier.PUBLIC;
  }

  private boolean isCompatible(Method src, Method dst) {
    if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) {
      return false;
    }
    Class<?>[] srcParams = src.getParameterTypes();
    Class<?>[] dstParams = dst.getParameterTypes();
    if (srcParams.length != dstParams.length) {
      return false;
    }
    for (int i = 0; i < srcParams.length; i++) {
      if (!srcParams[i].isAssignableFrom(dstParams[i])) {
        return false;
      }
    }
    return true;
  }

  @Override
  public F get() {
    return factory;
  }

  @Override
  public Set<Dependency<?>> getDependencies() {
    Set<Dependency<?>> combinedDeps = new HashSet<>();
    for (AssistData data : assistDataByMethod.values()) {
      combinedDeps.addAll(data.dependencies);
    }
    return ImmutableSet.copyOf(combinedDeps);
  }

  @Override
  public Key<F> getKey() {
    return factoryKey;
  }

  // Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
  // the collection is immutable.
  @Override
  @SuppressWarnings("unchecked")
  public Collection<AssistedMethod> getAssistedMethods() {
    return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T, V> V acceptExtensionVisitor(
      BindingTargetVisitor<T, V> visitor, ProviderInstanceBinding<? extends T> binding) {
    if (visitor instanceof AssistedInjectTargetVisitor) {
      return ((AssistedInjectTargetVisitor<T, V>) visitor).visit((AssistedInjectBinding<T>) this);
    }
    return visitor.visit(binding);
  }

  private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
    if (Modifier.isPublic(factoryType.getModifiers())
        && !Modifier.isPublic(returnType.getModifiers())) {
      errors.addMessage(
          "%s is public, but has a method that returns a non-public type: %s. "
              + "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
              + "Please either make the factory non-public or the return type public.",
          factoryType, returnType);
    }
  }

  /**
   * Returns true if the ConfigurationException is due to an error of TypeLiteral not being fully
   * specified.
   */
  private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
    Collection<Message> messages = ce.getErrorMessages();
    if (messages.size() == 1) {
      Message msg =
          Iterables.getOnlyElement(new Errors().keyNotFullySpecified(typeLiteral).getMessages());
      return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
    } else {
      return false;
    }
  }

  /**
   * Finds a constructor suitable for the method. If the implementation contained any constructors
   * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
   * match the parameters (in any order) listed in the method. Otherwise, if no {@link
   * AssistedInject} constructors exist, this will default to looking for an {@literal @}{@link
   * Inject} constructor.
   */
  private <T> InjectionPoint findMatchingConstructorInjectionPoint(
      Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
      throws ErrorsException {
    Errors errors = new Errors(method);
    if (returnType.getTypeLiteral().equals(implementation)) {
      errors = errors.withSource(implementation);
    } else {
      errors = errors.withSource(returnType).withSource(implementation);
    }

    Class<?> rawType = implementation.getRawType();
    if (Modifier.isInterface(rawType.getModifiers())) {
      errors.addMessage(
          "%s is an interface, not a concrete class.  Unable to create AssistedInject factory.",
          implementation);
      throw errors.toException();
    } else if (Modifier.isAbstract(rawType.getModifiers())) {
      errors.addMessage(
          "%s is abstract, not a concrete class.  Unable to create AssistedInject factory.",
          implementation);
      throw errors.toException();
    } else if (Classes.isInnerClass(rawType)) {
      errors.cannotInjectInnerClass(rawType);
      throw errors.toException();
    }

    Constructor<?> matchingConstructor = null;
    boolean anyAssistedInjectConstructors = false;
    // Look for AssistedInject constructors...
    for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
      if (constructor.isAnnotationPresent(AssistedInject.class)) {
        anyAssistedInjectConstructors = true;
        if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
          if (matchingConstructor != null) {
            errors.addMessage(
                "%s has more than one constructor annotated with @AssistedInject"
                    + " that matches the parameters in method %s.  Unable to create "
                    + "AssistedInject factory.",
                implementation, method);
            throw errors.toException();
          } else {
            matchingConstructor = constructor;
          }
        }
      }
    }

    if (!anyAssistedInjectConstructors) {
      // If none existed, use @Inject.
      try {
        return InjectionPoint.forConstructorOf(implementation);
      } catch (ConfigurationException e) {
        errors.merge(e.getErrorMessages());
        throw errors.toException();
      }
    } else {
      // Otherwise, use it or fail with a good error message.
      if (matchingConstructor != null) {
        // safe because we got the constructor from this implementation.
        @SuppressWarnings("unchecked")
        InjectionPoint ip =
            InjectionPoint.forConstructor(
                (Constructor<? super T>) matchingConstructor, implementation);
        return ip;
      } else {
        errors.addMessage(
            "%s has @AssistedInject constructors, but none of them match the"
                + " parameters in method %s.  Unable to create AssistedInject factory.",
            implementation, method);
        throw errors.toException();
      }
    }
  }

  /**
   * Matching logic for constructors annotated with AssistedInject. This returns true if and only if
   * all @Assisted parameters in the constructor exactly match (in any order) all @Assisted
   * parameters the method's parameter.
   */
  private boolean constructorHasMatchingParams(
      TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
      throws ErrorsException {
    List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
    Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
    int p = 0;
    List<Key<?>> constructorKeys = Lists.newArrayList();
    for (TypeLiteral<?> param : params) {
      Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++], errors);
      constructorKeys.add(paramKey);
    }
    // Require that every key exist in the constructor to match up exactly.
    for (Key<?> key : paramList) {
      // If it didn't exist in the constructor set, we can't use it.
      if (!constructorKeys.remove(key)) {
        return false;
      }
    }
    // If any keys remain and their annotation is Assisted, we can't use it.
    for (Key<?> key : constructorKeys) {
      if (key.getAnnotationType() == Assisted.class) {
        return false;
      }
    }
    // All @Assisted params match up to the method's parameters.
    return true;
  }

  /** Calculates all dependencies required by the implementation and constructor. */
  private Set<Dependency<?>> getDependencies(
      InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
    builder.addAll(ctorPoint.getDependencies());
    if (!implementation.getRawType().isInterface()) {
      for (InjectionPoint ip : InjectionPoint.forInstanceMethodsAndFields(implementation)) {
        builder.addAll(ip.getDependencies());
      }
    }
    return builder.build();
  }

  /** Return all non-assisted dependencies. */
  private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
    ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
    for (Dependency<?> dep : deps) {
      Class<?> annotationType = dep.getKey().getAnnotationType();
      if (annotationType == null || !annotationType.equals(Assisted.class)) {
        builder.add(dep);
      }
    }
    return builder.build();
  }

  /**
   * Returns true if all dependencies are suitable for the optimized version of AssistedInject. The
   * optimized version caches the binding & uses a ThreadLocal Provider, so can only be applied if
   * the assisted bindings are immediately provided. This looks for hints that the values may be
   * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
   */
  private boolean isValidForOptimizedAssistedInject(
      Set<Dependency<?>> dependencies, Class<?> implementation, TypeLiteral<?> factoryType) {
    Set<Dependency<?>> badDeps = null; // optimization: create lazily
    for (Dependency<?> dep : dependencies) {
      if (isInjectorOrAssistedProvider(dep)) {
        if (badDeps == null) {
          badDeps = Sets.newHashSet();
        }
        badDeps.add(dep);
      }
    }
    if (badDeps != null && !badDeps.isEmpty()) {
      logger.log(
          Level.WARNING,
          "AssistedInject factory {0} will be slow "
              + "because {1} has assisted Provider dependencies or injects the Injector. "
              + "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
              + "or Injector to speed things up. (It will be a ~6500% speed bump!)  "
              + "The exact offending deps are: {2}",
          new Object[] {factoryType, implementation, badDeps});
      return false;
    }
    return true;
  }

  /**
   * Returns true if the dependency is for {@link Injector} or if the dependency is a {@link
   * Provider} for a parameter that is {@literal @}{@link Assisted}.
   */
  private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
    Class<?> annotationType = dependency.getKey().getAnnotationType();
    if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
      if (dependency
          .getKey()
          .getTypeLiteral()
          .getRawType()
          .equals(Provider.class)) { // And a Provider...
        return true;
      }
    } else if (dependency
        .getKey()
        .getTypeLiteral()
        .getRawType()
        .equals(Injector.class)) { // If it's the Injector...
      return true;
    }
    return false;
  }

  /**
   * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. This
   * fails if another binding annotation is clobbered in the process. If the key already has the
   * {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
   */
  private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
    if (key.getAnnotationType() == null) {
      return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
    } else if (key.getAnnotationType() == Assisted.class) {
      return key;
    } else {
      errors
          .withSource(method)
          .addMessage(
              "Only @Assisted is allowed for factory parameters, but found @%s",
              key.getAnnotationType());
      throw errors.toException();
    }
  }

  /**
   * At injector-creation time, we initialize the invocation handler. At this time we make sure all
   * factory methods will be able to build the target types.
   */
  @Inject
  @Toolable
  void initialize(Injector injector) {
    if (this.injector != null) {
      throw new ConfigurationException(
          ImmutableList.of(
              new Message(
                  FactoryProvider2.class,
                  "Factories.create() factories may only be used in one Injector!")));
    }

    this.injector = injector;

    for (Map.Entry<Method, AssistData> entry : assistDataByMethod.entrySet()) {
      Method method = entry.getKey();
      AssistData data = entry.getValue();
      Object[] args;
      if (!data.optimized) {
        args = new Object[method.getParameterTypes().length];
        Arrays.fill(args, "dummy object for validating Factories");
      } else {
        args = null; // won't be used -- instead will bind to data.providers.
      }
      getBindingFromNewInjector(
          method, args, data); // throws if the binding isn't properly configured
    }
  }

  /**
   * Creates a child injector that binds the args, and returns the binding for the method's result.
   */
  public Binding<?> getBindingFromNewInjector(
      final Method method, final Object[] args, final AssistData data) {
    checkState(
        injector != null,
        "Factories.create() factories cannot be used until they're initialized by Guice.");

    final Key<?> returnType = data.returnType;

    // We ignore any pre-existing binding annotation.
    final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);

    Module assistedModule =
        new AbstractModule() {
          @Override
          @SuppressWarnings({
            "unchecked",
            "rawtypes"
          }) // raw keys are necessary for the args array and return value
          protected void configure() {
            Binder binder = binder().withSource(method);

            int p = 0;
            if (!data.optimized) {
              for (Key<?> paramKey : data.paramTypes) {
                // Wrap in a Provider to cover null, and to prevent Guice from injecting the
                // parameter
                binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
              }
            } else {
              for (Key<?> paramKey : data.paramTypes) {
                // Bind to our ThreadLocalProviders.
                binder.bind((Key) paramKey).toProvider(data.providers.get(p++));
              }
            }

            Constructor constructor = data.constructor;
            // Constructor *should* always be non-null here,
            // but if it isn't, we'll end up throwing a fairly good error
            // message for the user.
            if (constructor != null) {
              binder
                  .bind(returnKey)
                  .toConstructor(constructor, (TypeLiteral) data.implementationType)
                  .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
            }
          }
        };

    Injector forCreate = injector.createChildInjector(assistedModule);
    Binding<?> binding = forCreate.getBinding(returnKey);
    // If we have providers cached in data, cache the binding for future optimizations.
    if (data.optimized) {
      data.cachedBinding = binding;
    }
    return binding;
  }

  /**
   * When a factory method is invoked, we create a child injector that binds all parameters, then
   * use that to get an instance of the return type.
   */
  @Override
  public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
    // If we setup a method handle earlier for this method, call it.
    // This is necessary for default methods that java8 creates, so we
    // can call the default method implementation (and not our proxied version of it).
    if (methodHandleByMethod.containsKey(method)) {
      return methodHandleByMethod.get(method).invokeWithArguments(args);
    }

    if (method.getDeclaringClass().equals(Object.class)) {
      if ("equals".equals(method.getName())) {
        return proxy == args[0];
      } else if ("hashCode".equals(method.getName())) {
        return System.identityHashCode(proxy);
      } else {
        return method.invoke(this, args);
      }
    }

    AssistData data = assistDataByMethod.get(method);
    checkState(data != null, "No data for method: %s", method);
    Provider<?> provider;
    if (data.cachedBinding != null) { // Try to get optimized form...
      provider = data.cachedBinding.getProvider();
    } else {
      provider = getBindingFromNewInjector(method, args, data).getProvider();
    }
    try {
      int p = 0;
      for (ThreadLocalProvider tlp : data.providers) {
        tlp.set(args[p++]);
      }
      return provider.get();
    } catch (ProvisionException e) {
      // if this is an exception declared by the factory method, throw it as-is
      if (e.getErrorMessages().size() == 1) {
        Message onlyError = getOnlyElement(e.getErrorMessages());
        Throwable cause = onlyError.getCause();
        if (cause != null && canRethrow(method, cause)) {
          throw cause;
        }
      }
      throw e;
    } finally {
      for (ThreadLocalProvider tlp : data.providers) {
        tlp.remove();
      }
    }
  }

  @Override
  public String toString() {
    return factory.getClass().getInterfaces()[0].getName();
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(factoryKey, collector);
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof FactoryProvider2)) {
      return false;
    }
    FactoryProvider2<?> other = (FactoryProvider2<?>) obj;
    return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
  }

  /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
  static boolean canRethrow(Method invoked, Throwable thrown) {
    if (thrown instanceof Error || thrown instanceof RuntimeException) {
      return true;
    }

    for (Class<?> declared : invoked.getExceptionTypes()) {
      if (declared.isInstance(thrown)) {
        return true;
      }
    }

    return false;
  }

  // not <T> because we'll never know and this is easier than suppressing warnings.
  private static class ThreadLocalProvider extends ThreadLocal<Object> implements Provider<Object> {
    @Override
    protected Object initialValue() {
      throw new IllegalStateException(
          "Cannot use optimized @Assisted provider outside the scope of the constructor."
              + " (This should never happen.  If it does, please report it.)");
    }
  }

  // Note: this isn't a public API, but we need to use it in order to call default methods on (or
  // with) non-public types.  If it doesn't exist, the code falls back to a less precise check.
  private static final Constructor<MethodHandles.Lookup> methodHandlesLookupCxtor =
      findMethodHandlesLookupCxtor();

  private static Constructor<MethodHandles.Lookup> findMethodHandlesLookupCxtor() {
    try {
      Constructor<MethodHandles.Lookup> cxtor =
          MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
      cxtor.setAccessible(true);
      return cxtor;
    } catch (ReflectiveOperationException ignored) {
      // Ignore, the code falls back to a less-precise check if we can't create method handles.
      return null;
    }
  }

  private static MethodHandle createMethodHandle(Method method, Object proxy) {
    if (methodHandlesLookupCxtor == null) {
      return null;
    }
    Class<?> declaringClass = method.getDeclaringClass();
    int allModes =
        Modifier.PRIVATE | Modifier.STATIC /* package */ | Modifier.PUBLIC | Modifier.PROTECTED;
    try {
      MethodHandles.Lookup lookup = methodHandlesLookupCxtor.newInstance(declaringClass, allModes);
      method.setAccessible(true);
      return lookup.unreflectSpecial(method, declaringClass).bindTo(proxy);
    } catch (ReflectiveOperationException roe) {
      throw new RuntimeException("Unable to access method: " + method, roe);
    }
  }
}


/*
 * 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.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Primitives;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.ProvisionException;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.Message;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A collection of error messages. If this type is passed as a method parameter, the method is
 * considered to have executed successfully only if new errors were not added to this collection.
 *
 *
Errors can be chained to provide additional context. To add context, call {@link #withSource}
 * to create a new Errors instance that contains additional context. All messages added to the
 * returned instance will contain full context.
 *
 *
To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
 * good rule of thumb is to assume a method's caller has already specified enough context to
 * identify that method. When calling a method that's defined in a different context, call that
 * method with an errors object that includes its context.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
public final class Errors implements Serializable {

  /** When a binding is not found, show at most this many bindings with the same type */
  private static final int MAX_MATCHING_TYPES_REPORTED = 3;

  /** When a binding is not found, show at most this many bindings that have some similarities */
  private static final int MAX_RELATED_TYPES_REPORTED = 3;

  /**
   * Throws a ConfigurationException with an NullPointerExceptions as the cause if the given
   * reference is {@code null}.
   */
  static <T> T checkNotNull(T reference, String name) {
    if (reference != null) {
      return reference;
    }

    NullPointerException npe = new NullPointerException(name);
    throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
  }

  /**
   * Throws a ConfigurationException with a formatted {@link Message} if this condition is {@code
   * false}.
   */
  static void checkConfiguration(boolean condition, String format, Object... args) {
    if (condition) {
      return;
    }

    throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
  }

  /**
   * If the key is unknown and it is one of these types, it generally means there is a missing
   * annotation.
   */
  private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
      ImmutableSet.<Class<?>>builder()
          .add(Object.class)
          .add(String.class)
          .addAll(Primitives.allWrapperTypes())
          .build();

  /** The root errors object. Used to access the list of error messages. */
  private final Errors root;

  /** The parent errors object. Used to obtain the chain of source objects. */
  private final Errors parent;

  /** The leaf source for errors added here. */
  private final Object source;

  /** null unless (root == this) and error messages exist. Never an empty list. */
  private List<Message> errors; // lazy, use getErrorsForAdd()

  public Errors() {
    this.root = this;
    this.parent = null;
    this.source = SourceProvider.UNKNOWN_SOURCE;
  }

  public Errors(Object source) {
    this.root = this;
    this.parent = null;
    this.source = source;
  }

  private Errors(Errors parent, Object source) {
    this.root = parent.root;
    this.parent = parent;
    this.source = source;
  }

  /** Returns an instance that uses {@code source} as a reference point for newly added errors. */
  public Errors withSource(Object source) {
    return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
        ? this
        : new Errors(this, source);
  }

  /**
   * We use a fairly generic error message here. The motivation is to share the same message for
   * both bind time errors:
   *
   *
Guice.createInjector(new AbstractModule() {
   *   public void configure() {
   *     bind(Runnable.class);
   *   }
   * }

   *
   * ...and at provide-time errors:
   *
   *
Guice.createInjector().getInstance(Runnable.class);

   *
   * Otherwise we need to know who's calling when resolving a just-in-time binding, which makes
   * things unnecessarily complex.
   */
  public Errors missingImplementation(Key key) {
    return addMessage("No implementation for %s was bound.", key);
  }

  /** Within guice's core, allow for better missing binding messages */
  <T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
    StringBuilder sb = new StringBuilder();

    sb.append(format("No implementation for %s was bound.", key));

    // Keys which have similar strings as the desired key
    List<String> possibleMatches = new ArrayList<>();

    // Check for other keys that may have the same type,
    // but not the same annotation
    TypeLiteral<T> type = key.getTypeLiteral();
    List<Binding<T>> sameTypes = injector.findBindingsByType(type);
    if (!sameTypes.isEmpty()) {
      sb.append(format("%n  Did you mean?"));
      int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
      for (int i = 0; i < howMany; ++i) {
        // TODO: Look into a better way to prioritize suggestions. For example, possbily
        // use levenshtein distance of the given annotation vs actual annotation.
        sb.append(format("%n    * %s", sameTypes.get(i).getKey()));
      }
      int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
      if (remaining > 0) {
        String plural = (remaining == 1) ? "" : "s";
        sb.append(format("%n    %d more binding%s with other annotations.", remaining, plural));
      }
    } else {
      // For now, do a simple substring search for possibilities. This can help spot
      // issues when there are generics being used (such as a wrapper class) and the
      // user has forgotten they need to bind based on the wrapper, not the underlying
      // class. In the future, consider doing a strict in-depth type search.
      // TODO: Look into a better way to prioritize suggestions. For example, possbily
      // use levenshtein distance of the type literal strings.
      String want = type.toString();
      Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
      for (Key<?> bindingKey : bindingMap.keySet()) {
        String have = bindingKey.getTypeLiteral().toString();
        if (have.contains(want) || want.contains(have)) {
          Formatter fmt = new Formatter();
          Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
          String match = String.format("%s bound%s", convert(bindingKey), fmt.toString());
          possibleMatches.add(match);
          // TODO: Consider a check that if there are more than some number of results,
          // don't suggest any.
          if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
            // Early exit if we have found more than we need.
            break;
          }
        }
      }

      if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
        sb.append(format("%n  Did you mean?"));
        for (String possibleMatch : possibleMatches) {
          sb.append(format("%n    %s", possibleMatch));
        }
      }
    }

    // If where are no possibilities to suggest, then handle the case of missing
    // annotations on simple types. This is usually a bad idea.
    if (sameTypes.isEmpty()
        && possibleMatches.isEmpty()
        && key.getAnnotation() == null
        && COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
      // We don't recommend using such simple types without annotations.
      sb.append(format("%nThe key seems very generic, did you forget an annotation?"));
    }

    return addMessage(sb.toString());
  }

  public Errors jitDisabled(Key<?> key) {
    return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
  }

  public Errors jitDisabledInParent(Key<?> key) {
    return addMessage(
        "Explicit bindings are required and %s would be bound in a parent injector.%n"
            + "Please add an explicit binding for it, either in the child or the parent.",
        key);
  }

  public Errors atInjectRequired(Class clazz) {
    return addMessage(
        "Explicit @Inject annotations are required on constructors,"
            + " but %s has no constructors annotated with @Inject.",
        clazz);
  }

  public Errors converterReturnedNull(
      String stringValue,
      Object source,
      TypeLiteral<?> type,
      TypeConverterBinding typeConverterBinding) {
    return addMessage(
        "Received null converting '%s' (bound at %s) to %s%n using %s.",
        stringValue, convert(source), type, typeConverterBinding);
  }

  public Errors conversionTypeError(
      String stringValue,
      Object source,
      TypeLiteral<?> type,
      TypeConverterBinding typeConverterBinding,
      Object converted) {
    return addMessage(
        "Type mismatch converting '%s' (bound at %s) to %s%n"
            + " using %s.%n"
            + " Converter returned %s.",
        stringValue, convert(source), type, typeConverterBinding, converted);
  }

  public Errors conversionError(
      String stringValue,
      Object source,
      TypeLiteral<?> type,
      TypeConverterBinding typeConverterBinding,
      RuntimeException cause) {
    return errorInUserCode(
        cause,
        "Error converting '%s' (bound at %s) to %s%n using %s.%n Reason: %s",
        stringValue,
        convert(source),
        type,
        typeConverterBinding,
        cause);
  }

  public Errors ambiguousTypeConversion(
      String stringValue,
      Object source,
      TypeLiteral<?> type,
      TypeConverterBinding a,
      TypeConverterBinding b) {
    return addMessage(
        "Multiple converters can convert '%s' (bound at %s) to %s:%n"
            + " %s and%n"
            + " %s.%n"
            + " Please adjust your type converter configuration to avoid overlapping matches.",
        stringValue, convert(source), type, a, b);
  }

  public Errors bindingToProvider() {
    return addMessage("Binding to Provider is not allowed.");
  }

  public Errors notASubtype(Class<?> implementationType, Class<?> type) {
    return addMessage("%s doesn't extend %s.", implementationType, type);
  }

  public Errors recursiveImplementationType() {
    return addMessage("@ImplementedBy points to the same class it annotates.");
  }

  public Errors recursiveProviderType() {
    return addMessage("@ProvidedBy points to the same class it annotates.");
  }

  public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
    return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
  }

  public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
    return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
  }

  public Errors optionalConstructor(Constructor constructor) {
    return addMessage(
        "%s is annotated @Inject(optional=true), but constructors cannot be optional.",
        constructor);
  }

  public Errors cannotBindToGuiceType(String simpleName) {
    return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
  }

  public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
    return addMessage("No scope is bound to %s.", scopeAnnotation);
  }

  public Errors scopeAnnotationOnAbstractType(
      Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
    return addMessage(
        "%s is annotated with %s, but scope annotations are not supported "
            + "for abstract types.%n Bound at %s.",
        type, scopeAnnotation, convert(source));
  }

  public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
    return addMessage(
        "%s is annotated with %s, but binding annotations should be applied "
            + "to its parameters instead.",
        member, bindingAnnotation);
  }

  private static final String CONSTRUCTOR_RULES =
      "Classes must have either one (and only one) constructor "
          + "annotated with @Inject or a zero-argument constructor that is not private.";

  public Errors missingConstructor(Class<?> implementation) {
    return addMessage(
        "Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, implementation);
  }

  public Errors tooManyConstructors(Class<?> implementation) {
    return addMessage(
        "%s has more than one constructor annotated with @Inject. " + CONSTRUCTOR_RULES,
        implementation);
  }

  public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
    return addMessage("%s does not define %s", type, constructor);
  }

  public Errors duplicateScopes(
      ScopeBinding existing, Class<? extends Annotation> annotationType, Scope scope) {
    return addMessage(
        "Scope %s is already bound to %s at %s.%n Cannot bind %s.",
        existing.getScope(), annotationType, existing.getSource(), scope);
  }

  public Errors voidProviderMethod() {
    return addMessage("Provider methods must return a value. Do not return void.");
  }

  public Errors missingConstantValues() {
    return addMessage("Missing constant value. Please call to(...).");
  }

  public Errors cannotInjectInnerClass(Class<?> type) {
    return addMessage(
        "Injecting into inner classes is not supported.  "
            + "Please use a 'static' class (top-level or nested) instead of %s.",
        type);
  }

  public Errors duplicateBindingAnnotations(
      Member member, Class<? extends Annotation> a, Class<? extends Annotation> b) {
    return addMessage(
        "%s has more than one annotation annotated with @BindingAnnotation: %s and %s",
        member, a, b);
  }

  public Errors staticInjectionOnInterface(Class<?> clazz) {
    return addMessage("%s is an interface, but interfaces have no static injection points.", clazz);
  }

  public Errors cannotInjectFinalField(Field field) {
    return addMessage("Injected field %s cannot be final.", field);
  }

  public Errors cannotInjectAbstractMethod(Method method) {
    return addMessage("Injected method %s cannot be abstract.", method);
  }

  public Errors cannotInjectNonVoidMethod(Method method) {
    return addMessage("Injected method %s must return void.", method);
  }

  public Errors cannotInjectMethodWithTypeParameters(Method method) {
    return addMessage("Injected method %s cannot declare type parameters of its own.", method);
  }

  public Errors duplicateScopeAnnotations(
      Class<? extends Annotation> a, Class<? extends Annotation> b) {
    return addMessage("More than one scope annotation was found: %s and %s.", a, b);
  }

  public Errors recursiveBinding() {
    return addMessage("Binding points to itself.");
  }

  public Errors bindingAlreadySet(Key<?> key, Object source) {
    return addMessage("A binding to %s was already configured at %s.", key, convert(source));
  }

  public Errors jitBindingAlreadySet(Key<?> key) {
    return addMessage(
        "A just-in-time binding to %s was already configured on a parent injector.", key);
  }

  public Errors childBindingAlreadySet(Key<?> key, Set<Object> sources) {
    Formatter allSources = new Formatter();
    for (Object source : sources) {
      if (source == null) {
        allSources.format("%n    (bound by a just-in-time binding)");
      } else {
        allSources.format("%n    bound at %s", source);
      }
    }
    Errors errors =
        addMessage(
            "Unable to create binding for %s."
                + " It was already configured on one or more child injectors or private modules"
                + "%s%n"
                + "  If it was in a PrivateModule, did you forget to expose the binding?",
            key, allSources.out());
    return errors;
  }

  public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
    return addMessage(
        "A binding to %s was already configured at %s and an error was thrown "
            + "while checking duplicate bindings.  Error: %s",
        key, convert(source), t);
  }

  public Errors errorNotifyingTypeListener(
      TypeListenerBinding listener, TypeLiteral<?> type, Throwable cause) {
    return errorInUserCode(
        cause,
        "Error notifying TypeListener %s (bound at %s) of %s.%n Reason: %s",
        listener.getListener(),
        convert(listener.getSource()),
        type,
        cause);
  }

  public Errors exposedButNotBound(Key<?> key) {
    return addMessage("Could not expose() %s, it must be explicitly bound.", key);
  }

  public Errors keyNotFullySpecified(TypeLiteral<?> typeLiteral) {
    return addMessage("%s cannot be used as a key; It is not fully specified.", typeLiteral);
  }

  public Errors errorEnhancingClass(Class<?> clazz, Throwable cause) {
    return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
  }

  public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
    if (throwable instanceof ProvisionException) {
      return ((ProvisionException) throwable).getErrorMessages();
    } else if (throwable instanceof ConfigurationException) {
      return ((ConfigurationException) throwable).getErrorMessages();
    } else if (throwable instanceof CreationException) {
      return ((CreationException) throwable).getErrorMessages();
    } else {
      return ImmutableSet.of();
    }
  }

  public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
    Collection<Message> messages = getMessagesFromThrowable(cause);

    if (!messages.isEmpty()) {
      return merge(messages);
    } else {
      return addMessage(cause, messageFormat, arguments);
    }
  }

  public Errors cannotInjectRawProvider() {
    return addMessage("Cannot inject a Provider that has no type parameter");
  }

  public Errors cannotInjectRawMembersInjector() {
    return addMessage("Cannot inject a MembersInjector that has no type parameter");
  }

  public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
    return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
  }

  public Errors cannotInjectRawTypeLiteral() {
    return addMessage("Cannot inject a TypeLiteral that has no type parameter");
  }

  public void throwCreationExceptionIfErrorsExist() {
    if (!hasErrors()) {
      return;
    }

    throw new CreationException(getMessages());
  }

  public void throwConfigurationExceptionIfErrorsExist() {
    if (!hasErrors()) {
      return;
    }

    throw new ConfigurationException(getMessages());
  }

  // Guice no longer calls this, but external callers do
  public void throwProvisionExceptionIfErrorsExist() {
    if (!hasErrors()) {
      return;
    }

    throw new ProvisionException(getMessages());
  }

  public Errors merge(Collection<Message> messages) {
    List<Object> sources = getSources();
    for (Message message : messages) {
      addMessage(Messages.mergeSources(sources, message));
    }
    return this;
  }

  public Errors merge(Errors moreErrors) {
    if (moreErrors.root == root || moreErrors.root.errors == null) {
      return this;
    }

    merge(moreErrors.root.errors);
    return this;
  }

  public Errors merge(InternalProvisionException ipe) {
    merge(ipe.getErrors());
    return this;
  }

  private List<Object> getSources() {
    List<Object> sources = Lists.newArrayList();
    for (Errors e = this; e != null; e = e.parent) {
      if (e.source != SourceProvider.UNKNOWN_SOURCE) {
        sources.add(0, e.source);
      }
    }
    return sources;
  }

  public void throwIfNewErrors(int expectedSize) throws ErrorsException {
    if (size() == expectedSize) {
      return;
    }

    throw toException();
  }

  public ErrorsException toException() {
    return new ErrorsException(this);
  }

  public boolean hasErrors() {
    return root.errors != null;
  }

  public Errors addMessage(String messageFormat, Object... arguments) {
    return addMessage(null, messageFormat, arguments);
  }

  private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
    addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
    return this;
  }

  public Errors addMessage(Message message) {
    if (root.errors == null) {
      root.errors = Lists.newArrayList();
    }
    root.errors.add(message);
    return this;
  }

  // TODO(lukes): inline into callers
  public static String format(String messageFormat, Object... arguments) {
    return Messages.format(messageFormat, arguments);
  }

  public List<Message> getMessages() {
    if (root.errors == null) {
      return ImmutableList.of();
    }

    return new Ordering<Message>() {
      @Override
      public int compare(Message a, Message b) {
        return a.getSource().compareTo(b.getSource());
      }
    }.sortedCopy(root.errors);
  }

  public int size() {
    return root.errors == null ? 0 : root.errors.size();
  }

  // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
  public static Object convert(Object o) {
    return Messages.convert(o);
  }

  // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
  public static Object convert(Object o, ElementSource source) {
    return Messages.convert(o, source);
  }

  // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
  public static void formatSource(Formatter formatter, Object source) {
    Messages.formatSource(formatter, source);
  }

}


package com.google.inject.internal;

import static com.google.inject.internal.Element.Type.MULTIBINDER;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.internal.Errors.checkNotNull;
import static com.google.inject.name.Names.named;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
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.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MultibinderBinding;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * The actual multibinder plays several roles:
 *
 *
As a Multibinder, it acts as a factory for LinkedBindingBuilders for each of the set's
 * elements. Each binding is given an annotation that identifies it as a part of this set.
 *
 *
As a Module, it installs the binding to the set itself. As a module, this implements equals()
 * and hashcode() in order to trick Guice into executing its configure() method only once. That
 * makes it so that multiple multibinders can be created for the same target collection, but only
 * one is bound. Since the list of bindings is retrieved from the injector itself (and not the
 * multibinder), each multibinder has access to all contributions from all multibinders.
 *
 *
As a Provider, this constructs the set instances.
 *
 *
We use a subclass to hide 'implements Module, Provider' from the public API.
 */
public final class RealMultibinder<T> implements Module {

  /** Implementation of newSetBinder. */
  public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
    binder = binder.skipSources(RealMultibinder.class);
    RealMultibinder<T> result = new RealMultibinder<>(binder, key);
    binder.install(result);
    return result;
  }

  @SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
  static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
    Type type = Types.setOf(elementType.getType());
    return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
  }

  @SuppressWarnings("unchecked")
  static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
      TypeLiteral<T> elementType) {
    Type providerType = Types.providerOf(elementType.getType());
    Type type = Types.collectionOf(providerType);
    return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
  }

  @SuppressWarnings("unchecked")
  static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
      TypeLiteral<T> elementType) {
    Type providerType =
        Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
    Type type = Types.collectionOf(providerType);
    return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
  }

  private final BindingSelection<T> bindingSelection;
  private final Binder binder;

  RealMultibinder(Binder binder, Key<T> key) {
    this.binder = checkNotNull(binder, "binder");
    this.bindingSelection = new BindingSelection<>(key);
  }

  @Override
  public void configure(Binder binder) {
    checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
    binder
        .bind(bindingSelection.getSetKey())
        .toProvider(new RealMultibinderProvider<T>(bindingSelection));
    Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
        new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
    binder
        .bind(bindingSelection.getCollectionOfProvidersKey())
        .toProvider(collectionOfProvidersProvider);

    // The collection this exposes is internally an ImmutableList, so it's OK to massage
    // the guice Provider to javax Provider in the value (since the guice Provider implements
    // javax Provider).
    @SuppressWarnings("unchecked")
    Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
        (Provider) collectionOfProvidersProvider;
    binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
  }

  public void permitDuplicates() {
    binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
  }

  /** Adds a new entry to the set and returns the key for it. */
  Key<T> getKeyForNewItem() {
    checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
    return Key.get(
        bindingSelection.getElementTypeLiteral(),
        new RealElement(bindingSelection.getSetName(), MULTIBINDER, ""));
  }

  public LinkedBindingBuilder<T> addBinding() {
    return binder.bind(getKeyForNewItem());
  }

  // These methods are used by RealMapBinder

  Key<Set<T>> getSetKey() {
    return bindingSelection.getSetKey();
  }

  TypeLiteral<T> getElementTypeLiteral() {
    return bindingSelection.getElementTypeLiteral();
  }

  String getSetName() {
    return bindingSelection.getSetName();
  }

  boolean permitsDuplicates(Injector injector) {
    return bindingSelection.permitsDuplicates(injector);
  }

  boolean containsElement(com.google.inject.spi.Element element) {
    return bindingSelection.containsElement(element);
  }

  private static final class RealMultibinderProvider<T>
      extends InternalProviderInstanceBindingImpl.Factory<Set<T>>
      implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
    private final BindingSelection<T> bindingSelection;
    private List<Binding<T>> bindings;
    private SingleParameterInjector<T>[] injectors;
    private boolean permitDuplicates;

    RealMultibinderProvider(BindingSelection<T> bindingSelection) {
      // While Multibinders only depend on bindings created in modules so we could theoretically
      // initialize eagerly, they also depend on
      // 1. findBindingsByType returning results
      // 2. being able to call BindingImpl.acceptTargetVisitor
      // neither of those is available during eager initialization, so we use DELAYED
      super(InitializationTiming.DELAYED);
      this.bindingSelection = bindingSelection;
    }

    @Override
    public Set<Dependency<?>> getDependencies() {
      return bindingSelection.getDependencies();
    }

    @Override
    void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
      bindingSelection.initialize(injector, errors);
      this.bindings = bindingSelection.getBindings();
      this.injectors = bindingSelection.getParameterInjectors();
      this.permitDuplicates = bindingSelection.permitsDuplicates();
    }

    @Override
    protected Set<T> doProvision(InternalContext context, Dependency<?> dependency)
        throws InternalProvisionException {
      SingleParameterInjector<T>[] localInjectors = injectors;
      if (localInjectors == null) {
        // if localInjectors == null, then we have no bindings so return the empty set.
        return ImmutableSet.of();
      }
      // Ideally we would just add to an ImmutableSet.Builder, but if we did that and there were
      // duplicates we wouldn't be able to tell which one was the duplicate.  So to manage this we
      // first put everything into an array and then construct the set.  This way if something gets
      // dropped we can figure out what it is.
      @SuppressWarnings("unchecked")
      T[] values = (T[]) new Object[localInjectors.length];
      for (int i = 0; i < localInjectors.length; i++) {
        SingleParameterInjector<T> parameterInjector = localInjectors[i];
        T newValue = parameterInjector.inject(context);
        if (newValue == null) {
          throw newNullEntryException(i);
        }
        values[i] = newValue;
      }
      ImmutableSet<T> set = ImmutableSet.copyOf(values);
      // There are fewer items in the set than the array.  Figure out which one got dropped.
      if (!permitDuplicates && set.size() < values.length) {
        throw newDuplicateValuesException(set, values);
      }
      return set;
    }

    private InternalProvisionException newNullEntryException(int i) {
      return InternalProvisionException.create(
          "Set injection failed due to null element bound at: %s", bindings.get(i).getSource());
    }

    @SuppressWarnings("unchecked")
    @Override
    public  V acceptExtensionVisitor(
        BindingTargetVisitor visitor, ProviderInstanceBinding<? extends B> binding) {
      if (visitor instanceof MultibindingsTargetVisitor) {
        return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
      } else {
        return visitor.visit(binding);
      }
    }

    private InternalProvisionException newDuplicateValuesException(
        ImmutableSet<T> set, T[] values) {
      // TODO(lukes): consider reporting all duplicate values, the easiest way would be to rebuild
      // a new set and detect dupes as we go
      // Find the duplicate binding
      // To do this we take advantage of the fact that set, values and bindings all have the same
      // ordering for a non-empty prefix of the set.
      // First we scan for the first item dropped from the set.
      int newBindingIndex = 0;
      for (T item : set) {
        if (item != values[newBindingIndex]) {
          break;
        }
        newBindingIndex++;
      }
      // once we exit the loop newBindingIndex will point at the first item in values that was
      // dropped.

      Binding<T> newBinding = bindings.get(newBindingIndex);
      T newValue = values[newBindingIndex];
      // Now we scan again to find the index of the value, we are guaranteed to find it.
      int oldBindingIndex = set.asList().indexOf(newValue);
      T oldValue = values[oldBindingIndex];
      Binding<T> duplicateBinding = bindings.get(oldBindingIndex);
      String oldString = oldValue.toString();
      String newString = newValue.toString();
      if (Objects.equal(oldString, newString)) {
        // When the value strings match, just show the source of the bindings
        return InternalProvisionException.create(
            "Set injection failed due to duplicated element \"%s\""
                + "\n    Bound at %s\n    Bound at %s",
            newValue, duplicateBinding.getSource(), newBinding.getSource());
      } else {
        // When the value strings don't match, include them both as they may be useful for debugging
        return InternalProvisionException.create(
            "Set injection failed due to multiple elements comparing equal:"
                + "\n    \"%s\"\n        bound at %s"
                + "\n    \"%s\"\n        bound at %s",
            oldValue, duplicateBinding.getSource(), newValue, newBinding.getSource());
      }
    }

    @Override
    public boolean equals(Object obj) {
      return obj instanceof RealMultibinderProvider
          && bindingSelection.equals(((RealMultibinderProvider<?>) obj).bindingSelection);
    }

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

    @Override
    public Key<Set<T>> getSetKey() {
      return bindingSelection.getSetKey();
    }

    @Override
    public Set<Key<?>> getAlternateSetKeys() {
      return ImmutableSet.of(
          (Key<?>) bindingSelection.getCollectionOfProvidersKey(),
          (Key<?>) bindingSelection.getCollectionOfJavaxProvidersKey());
    }

    @Override
    public TypeLiteral<?> getElementTypeLiteral() {
      return bindingSelection.getElementTypeLiteral();
    }

    @Override
    public List<Binding<?>> getElements() {
      return bindingSelection.getElements();
    }

    @Override
    public boolean permitsDuplicates() {
      return bindingSelection.permitsDuplicates();
    }

    @Override
    public boolean containsElement(com.google.inject.spi.Element element) {
      return bindingSelection.containsElement(element);
    }
  }

  private static final class BindingSelection<T> {
    // prior to initialization we declare just a dependency on the injector, but as soon as we are
    // initialized we swap to dependencies on the elements.
    private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
        ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
    private final TypeLiteral<T> elementType;
    private final Key<Set<T>> setKey;

    // these are all lazily allocated
    private String setName;
    private Key<Collection<Provider<T>>> collectionOfProvidersKey;
    private Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
    private Key<Boolean> permitDuplicatesKey;

    private boolean isInitialized;
    /* a binding for each element in the set. null until initialization, non-null afterwards */
    private ImmutableList<Binding<T>> bindings;

    // Starts out as Injector and gets set up properly after initialization
    private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
    private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;

    /** whether duplicates are allowed. Possibly configured by a different instance */
    private boolean permitDuplicates;

    private SingleParameterInjector<T>[] parameterinjectors;

    BindingSelection(Key<T> key) {
      this.setKey = key.ofType(setOf(key.getTypeLiteral()));
      this.elementType = key.getTypeLiteral();
    }

    void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
      // This will be called multiple times, once by each Factory. We only want
      // to do the work to initialize everything once, so guard this code with
      // isInitialized.
      if (isInitialized) {
        return;
      }
      List<Binding<T>> bindings = Lists.newArrayList();
      Set<Indexer.IndexedBinding> index = Sets.newHashSet();
      Indexer indexer = new Indexer(injector);
      List<Dependency<?>> dependencies = Lists.newArrayList();
      List<Dependency<?>> providerDependencies = Lists.newArrayList();
      for (Binding<?> entry : injector.findBindingsByType(elementType)) {
        if (keyMatches(entry.getKey())) {
          @SuppressWarnings("unchecked") // protected by findBindingsByType()
          Binding<T> binding = (Binding<T>) entry;
          if (index.add(binding.acceptTargetVisitor(indexer))) {
            // TODO(lukes): most of these are linked bindings since user bindings are linked to
            // a user binding through the @Element annotation.  Since this is an implementation
            // detail we could 'dereference' the @Element if it is a LinkedBinding and avoid
            // provisioning through the FactoryProxy at runtime.
            // Ditto for OptionalBinder/MapBinder
            bindings.add(binding);
            Key<T> key = binding.getKey();
            // TODO(lukes): we should mark this as a non-nullable dependency since we don't accept
            // null.
            // Add a dependency on Key<T>
            dependencies.add(Dependency.get(key));
            // and add a dependency on Key<Provider<T>>
            providerDependencies.add(
                Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
          }
        }
      }

      this.bindings = ImmutableList.copyOf(bindings);
      this.dependencies = ImmutableSet.copyOf(dependencies);
      this.providerDependencies = ImmutableSet.copyOf(providerDependencies);
      this.permitDuplicates = permitsDuplicates(injector);
      // This is safe because all our dependencies are assignable to T and we never assign to
      // elements of this array.
      @SuppressWarnings("unchecked")
      SingleParameterInjector<T>[] typed =
          (SingleParameterInjector<T>[]) injector.getParametersInjectors(dependencies, errors);
      this.parameterinjectors = typed;
      isInitialized = true;
    }

    boolean permitsDuplicates(Injector injector) {
      return injector.getBindings().containsKey(getPermitDuplicatesKey());
    }

    ImmutableList<Binding<T>> getBindings() {
      checkConfiguration(isInitialized, "not initialized");
      return bindings;
    }

    SingleParameterInjector<T>[] getParameterInjectors() {
      checkConfiguration(isInitialized, "not initialized");
      return parameterinjectors;
    }

    ImmutableSet<Dependency<?>> getDependencies() {
      return dependencies;
    }

    ImmutableSet<Dependency<?>> getProviderDependencies() {
      return providerDependencies;
    }

    String getSetName() {
      // lazily initialized since most selectors don't survive module installation.
      if (setName == null) {
        setName = Annotations.nameOf(setKey);
      }
      return setName;
    }

    Key<Boolean> getPermitDuplicatesKey() {
      Key<Boolean> local = permitDuplicatesKey;
      if (local == null) {
        local =
            permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
      }
      return local;
    }

    Key<Collection<Provider<T>>> getCollectionOfProvidersKey() {
      Key<Collection<Provider<T>>> local = collectionOfProvidersKey;
      if (local == null) {
        local = collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
      }
      return local;
    }

    Key<Collection<javax.inject.Provider<T>>> getCollectionOfJavaxProvidersKey() {
      Key<Collection<javax.inject.Provider<T>>> local = collectionOfJavaxProvidersKey;
      if (local == null) {
        local =
            collectionOfJavaxProvidersKey =
                setKey.ofType(collectionOfJavaxProvidersOf(elementType));
      }
      return local;
    }

    boolean isInitialized() {
      return isInitialized;
    }

    // MultibinderBinding API methods

    TypeLiteral<T> getElementTypeLiteral() {
      return elementType;
    }

    Key<Set<T>> getSetKey() {
      return setKey;
    }

    @SuppressWarnings("unchecked")
    List<Binding<?>> getElements() {
      if (isInitialized()) {
        return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
      } else {
        throw new UnsupportedOperationException("getElements() not supported for module bindings");
      }
    }

    boolean permitsDuplicates() {
      if (isInitialized()) {
        return permitDuplicates;
      } else {
        throw new UnsupportedOperationException(
            "permitsDuplicates() not supported for module bindings");
      }
    }

    boolean containsElement(com.google.inject.spi.Element element) {
      if (element instanceof Binding) {
        Binding<?> binding = (Binding<?>) element;
        return keyMatches(binding.getKey())
            || binding.getKey().equals(getPermitDuplicatesKey())
            || binding.getKey().equals(setKey)
            || binding.getKey().equals(collectionOfProvidersKey)
            || binding.getKey().equals(collectionOfJavaxProvidersKey);
      } else {
        return false;
      }
    }

    private boolean keyMatches(Key<?> key) {
      return key.getTypeLiteral().equals(elementType)
          && key.getAnnotation() instanceof Element
          && ((Element) key.getAnnotation()).setName().equals(getSetName())
          && ((Element) key.getAnnotation()).type() == MULTIBINDER;
    }

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof BindingSelection) {
        return setKey.equals(((BindingSelection<?>) obj).setKey);
      }
      return false;
    }

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

    @Override
    public String toString() {
      return (getSetName().isEmpty() ? "" : getSetName() + " ")
          + "Multibinder<"
          + elementType
          + ">";
    }
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof RealMultibinder
        && ((RealMultibinder<?>) o).bindingSelection.equals(bindingSelection);
  }

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

  private static final class RealMultibinderCollectionOfProvidersProvider<T>
      extends InternalProviderInstanceBindingImpl.Factory<Collection<Provider<T>>> {

    private final BindingSelection<T> bindingSelection;
    private ImmutableList<Provider<T>> collectionOfProviders;

    RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection) {
      super(InitializationTiming.DELAYED); // See comment in RealMultibinderProvider
      this.bindingSelection = bindingSelection;
    }

    @Override
    void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
      bindingSelection.initialize(injector, errors);
      ImmutableList.Builder<Provider<T>> providers = ImmutableList.builder();
      for (Binding<T> binding : bindingSelection.getBindings()) {
        providers.add(binding.getProvider());
      }
      this.collectionOfProviders = providers.build();
    }

    @Override
    protected Collection<Provider<T>> doProvision(
        InternalContext context, Dependency<?> dependency) {
      return collectionOfProviders;
    }

    @Override
    public Set<Dependency<?>> getDependencies() {
      return bindingSelection.getProviderDependencies();
    }

    @Override
    public boolean equals(Object obj) {
      return obj instanceof RealMultibinderCollectionOfProvidersProvider
          && bindingSelection.equals(
              ((RealMultibinderCollectionOfProvidersProvider<?>) obj).bindingSelection);
    }

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

  /**
   * We install the permit duplicates configuration as its own binding, all by itself. This way, if
   * only one of a multibinder's users remember to call permitDuplicates(), they're still permitted.
   *
   *
This is like setting a global variable in the injector so that each instance of the
   * multibinder will have the same value for permitDuplicates, even if it is only set on one of
   * them.
   */
  private static class PermitDuplicatesModule extends AbstractModule {
    private final Key<Boolean> key;

    PermitDuplicatesModule(Key<Boolean> key) {
      this.key = key;
    }

    @Override
    protected void configure() {
      bind(key).toInstance(true);
    }

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

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


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

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * A Filter chain impl which basically passes itself to the "current" filter and iterates the chain
 * on {@code doFilter()}. Modeled on something similar in Apache Tomcat.
 *
 *
Following this, it attempts to dispatch to guice-servlet's registered servlets using the
 * ManagedServletPipeline.
 *
 *
And the end, it proceeds to the web.xml (default) servlet filter chain, if needed.
 *
 * @author Dhanji R. Prasanna
 * @since 1.0
 */
class FilterChainInvocation implements FilterChain {

  private static final ImmutableSet<String> SERVLET_INTERNAL_METHODS =
      ImmutableSet.of(FilterChainInvocation.class.getName() + ".doFilter");

  private final FilterDefinition[] filterDefinitions;
  private final FilterChain proceedingChain;
  private final ManagedServletPipeline servletPipeline;

  //state variable tracks current link in filterchain
  private int index = -1;
  // whether or not we've caught an exception & cleaned up stack traces
  private boolean cleanedStacks = false;

  public FilterChainInvocation(
      FilterDefinition[] filterDefinitions,
      ManagedServletPipeline servletPipeline,
      FilterChain proceedingChain) {

    this.filterDefinitions = filterDefinitions;
    this.servletPipeline = servletPipeline;
    this.proceedingChain = proceedingChain;
  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
      throws IOException, ServletException {
    GuiceFilter.Context previous = GuiceFilter.localContext.get();
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    HttpServletRequest originalRequest =
        (previous != null) ? previous.getOriginalRequest() : request;
    GuiceFilter.localContext.set(new GuiceFilter.Context(originalRequest, request, response));
    try {
      Filter filter = findNextFilter(request);
      if (filter != null) {
        // call to the filter, which can either consume the request or
        // recurse back into this method. (in which case we will go to find the next filter,
        // or dispatch to the servlet if no more filters are left)
        filter.doFilter(servletRequest, servletResponse, this);
      } else {
        //we've reached the end of the filterchain, let's try to dispatch to a servlet
        final boolean serviced = servletPipeline.service(servletRequest, servletResponse);

        //dispatch to the normal filter chain only if one of our servlets did not match
        if (!serviced) {
          proceedingChain.doFilter(servletRequest, servletResponse);
        }
      }
    } catch (Throwable t) {
      // Only clean on the first pass through -- one exception deep in a filter
      // will propogate upward & hit this catch clause multiple times.  We don't
      // want to iterate through the stack elements for every filter.
      if (!cleanedStacks) {
        cleanedStacks = true;
        pruneStacktrace(t);
      }
      Throwables.propagateIfInstanceOf(t, ServletException.class);
      Throwables.propagateIfInstanceOf(t, IOException.class);
      throw Throwables.propagate(t);
    } finally {
      GuiceFilter.localContext.set(previous);
    }
  }

  /**
   * Iterates over the remaining filter definitions. Returns the first applicable filter, or null if
   * none apply.
   */
  private Filter findNextFilter(HttpServletRequest request) {
    while (++index < filterDefinitions.length) {
      Filter filter = filterDefinitions[index].getFilterIfMatching(request);
      if (filter != null) {
        return filter;
      }
    }
    return null;
  }

  /**
   * 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 name = element.getClassName() + "." + element.getMethodName();
        if (!SERVLET_INTERNAL_METHODS.contains(name)) {
          pruned.add(element);
        }
      }
      t.setStackTrace(pruned.toArray(new StackTraceElement[pruned.size()]));
    }
  }
}


/*
 * Copyright (C) 2007 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 static com.google.inject.internal.Annotations.findScopeAnnotation;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Set;

final class ConstructorBindingImpl<T> extends BindingImpl<T>
    implements ConstructorBinding<T>, DelayedInitialize {

  private final Factory<T> factory;
  private final InjectionPoint constructorInjectionPoint;

  private ConstructorBindingImpl(
      InjectorImpl injector,
      Key<T> key,
      Object source,
      InternalFactory<? extends T> scopedFactory,
      Scoping scoping,
      Factory<T> factory,
      InjectionPoint constructorInjectionPoint) {
    super(injector, key, source, scopedFactory, scoping);
    this.factory = factory;
    this.constructorInjectionPoint = constructorInjectionPoint;
  }

  public ConstructorBindingImpl(
      Key<T> key,
      Object source,
      Scoping scoping,
      InjectionPoint constructorInjectionPoint,
      Set<InjectionPoint> injectionPoints) {
    super(source, key, scoping);
    this.factory = new Factory<>(false, key);
    ConstructionProxy<T> constructionProxy =
        new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
    this.constructorInjectionPoint = constructorInjectionPoint;
    factory.constructorInjector =
        new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null);
  }

  /**
   * @param constructorInjector the constructor to use, or {@code null} to use the default.
   * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only
   *     succeed if retrieved from a linked binding
   */
  static <T> ConstructorBindingImpl<T> create(
      InjectorImpl injector,
      Key<T> key,
      InjectionPoint constructorInjector,
      Object source,
      Scoping scoping,
      Errors errors,
      boolean failIfNotLinked,
      boolean failIfNotExplicit)
      throws ErrorsException {
    int numErrors = errors.size();

    @SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
    Class<? super T> rawType =
        constructorInjector == null
            ? key.getTypeLiteral().getRawType()
            : (Class) constructorInjector.getDeclaringType().getRawType();

    // We can't inject abstract classes.
    if (Modifier.isAbstract(rawType.getModifiers())) {
      errors.missingImplementationWithHint(key, injector);
    }

    // Error: Inner class.
    if (Classes.isInnerClass(rawType)) {
      errors.cannotInjectInnerClass(rawType);
    }

    errors.throwIfNewErrors(numErrors);

    // Find a constructor annotated @Inject
    if (constructorInjector == null) {
      try {
        constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
        if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
          errors.atInjectRequired(rawType);
        }
      } catch (ConfigurationException e) {
        throw errors.merge(e.getErrorMessages()).toException();
      }
    }

    // if no scope is specified, look for a scoping annotation on the concrete class
    if (!scoping.isExplicitlyScoped()) {
      Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
      Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
      if (scopeAnnotation != null) {
        scoping =
            Scoping.makeInjectable(
                Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType));
      }
    }

    errors.throwIfNewErrors(numErrors);

    Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key);
    InternalFactory<? extends T> scopedFactory =
        Scoping.scope(key, injector, factoryFactory, source, scoping);

    return new ConstructorBindingImpl<T>(
        injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
  }

  /** Returns true if the inject annotation is on the constructor. */
  private static boolean hasAtInject(Constructor cxtor) {
    return cxtor.isAnnotationPresent(Inject.class)
        || cxtor.isAnnotationPresent(javax.inject.Inject.class);
  }

  @Override
  @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
  public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
    factory.constructorInjector =
        (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
    factory.provisionCallback = injector.provisionListenerStore.get(this);
  }

  /** True if this binding has been initialized and is ready for use. */
  boolean isInitialized() {
    return factory.constructorInjector != null;
  }

  /** Returns an injection point that can be used to clean up the constructor store. */
  InjectionPoint getInternalConstructor() {
    if (factory.constructorInjector != null) {
      return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
    } else {
      return constructorInjectionPoint;
    }
  }

  /** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */
  Set<Dependency<?>> getInternalDependencies() {
    ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
    if (factory.constructorInjector == null) {
      builder.add(constructorInjectionPoint);
      // If the below throws, it's OK -- we just ignore those dependencies, because no one
      // could have used them anyway.
      try {
        builder.addAll(
            InjectionPoint.forInstanceMethodsAndFields(
                constructorInjectionPoint.getDeclaringType()));
      } catch (ConfigurationException ignored) {
      }
    } else {
      builder.add(getConstructor()).addAll(getInjectableMembers());
    }

    return Dependency.forInjectionPoints(builder.build());
  }

  @Override
  public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
    checkState(factory.constructorInjector != null, "not initialized");
    return visitor.visit(this);
  }

  @Override
  public InjectionPoint getConstructor() {
    checkState(factory.constructorInjector != null, "Binding is not ready");
    return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
  }

  @Override
  public Set<InjectionPoint> getInjectableMembers() {
    checkState(factory.constructorInjector != null, "Binding is not ready");
    return factory.constructorInjector.getInjectableMembers();
  }

  /*if[AOP]*/
  @Override
  public Map<Method, List<org.aopalliance.intercept.MethodInterceptor>> getMethodInterceptors() {
    checkState(factory.constructorInjector != null, "Binding is not ready");
    return factory.constructorInjector.getConstructionProxy().getMethodInterceptors();
  }
  /*end[AOP]*/

  @Override
  public Set<Dependency<?>> getDependencies() {
    return Dependency.forInjectionPoints(
        new ImmutableSet.Builder<InjectionPoint>()
            .add(getConstructor())
            .addAll(getInjectableMembers())
            .build());
  }

  @Override
  protected BindingImpl<T> withScoping(Scoping scoping) {
    return new ConstructorBindingImpl<T>(
        null, getKey(), getSource(), factory, scoping, factory, constructorInjectionPoint);
  }

  @Override
  protected BindingImpl<T> withKey(Key<T> key) {
    return new ConstructorBindingImpl<T>(
        null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
  }

  @Override
  @SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
  public void applyTo(Binder binder) {
    InjectionPoint constructor = getConstructor();
    getScoping()
        .applyTo(
            binder
                .withSource(getSource())
                .bind(getKey())
                .toConstructor(
                    (Constructor) getConstructor().getMember(),
                    (TypeLiteral) constructor.getDeclaringType()));
  }

  @Override
  public String toString() {
    return MoreObjects.toStringHelper(ConstructorBinding.class)
        .add("key", getKey())
        .add("source", getSource())
        .add("scope", getScoping())
        .toString();
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ConstructorBindingImpl) {
      ConstructorBindingImpl<?> o = (ConstructorBindingImpl<?>) obj;
      return getKey().equals(o.getKey())
          && getScoping().equals(o.getScoping())
          && Objects.equal(constructorInjectionPoint, o.constructorInjectionPoint);
    } else {
      return false;
    }
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(getKey(), getScoping(), constructorInjectionPoint);
  }

  private static class Factory<T> implements InternalFactory<T> {
    private final boolean failIfNotLinked;
    private final Key<?> key;
    private ConstructorInjector<T> constructorInjector;
    private ProvisionListenerStackCallback<T> provisionCallback;

    Factory(boolean failIfNotLinked, Key<?> key) {
      this.failIfNotLinked = failIfNotLinked;
      this.key = key;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T get(InternalContext context, Dependency<?> dependency, boolean linked)
        throws InternalProvisionException {
      ConstructorInjector<T> localInjector = constructorInjector;
      if (localInjector == null) {
        throw new IllegalStateException("Constructor not ready");
      }

      if (!linked && failIfNotLinked) {
        throw InternalProvisionException.jitDisabled(key);
      }

      // This may not actually be safe because it could return a super type of T (if that's all the
      // client needs), but it should be OK in practice thanks to the wonders of erasure.
      return (T) localInjector.construct(context, dependency, provisionCallback);
    }
  }
}