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

import static com.google.inject.internal.RealMultibinder.newRealSetBinder;

import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.RealMultibinder;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Set;

/**
 * An API to bind multiple values separately, only to later inject them as a complete collection.
 * Multibinder is intended for use in your application's module:
 *
 *

 * public class SnacksModule extends AbstractModule {
 *   protected void configure() {
 *     Multibinder<Snack> multibinder
 *         = Multibinder.newSetBinder(binder(), Snack.class);
 *     multibinder.addBinding().toInstance(new Twix());
 *     multibinder.addBinding().toProvider(SnickersProvider.class);
 *     multibinder.addBinding().to(Skittles.class);
 *   }
 * }

 *
 *
With this binding, a {@link Set}{@code <Snack>} can now be injected:
 *
 *

 * class SnackMachine {
 *   {@literal @}Inject
 *   public SnackMachine(Set<Snack> snacks) { ... }
 * }

 *
 * If desired, {@link Collection}{@code <Provider<Snack>>} can also be injected.
 *
 *
Contributing multibindings from different modules is supported. For example, it is okay for
 * both {@code CandyModule} and {@code ChipsModule} to create their own {@code Multibinder<Snack>},
 * and to each contribute bindings to the set of snacks. When that set is injected, it will contain
 * elements from both modules.
 *
 *
The set's iteration order is consistent with the binding order. This is convenient when
 * multiple elements are contributed by the same module because that module can order its bindings
 * appropriately. Avoid relying on the iteration order of elements contributed by different modules,
 * since there is no equivalent mechanism to order modules.
 *
 *
The set is unmodifiable. Elements can only be added to the set by configuring the multibinder.
 * Elements can never be removed from the set.
 *
 *
Elements are resolved at set injection time. If an element is bound to a provider, that
 * provider's get method will be called each time the set is injected (unless the binding is also
 * scoped).
 *
 *
Annotations are be used to create different sets of the same element type. Each distinct
 * annotation gets its own independent collection of elements.
 *
 *
Elements must be distinct. If multiple bound elements have the same value,
 * set injection will fail.
 *
 *
Elements must be non-null. If any set element is null, set injection will
 * fail.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
public class Multibinder<T> {
  // This class is non-final due to users mocking this in tests :(

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with no binding annotation.
   */
  public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type) {
    return newSetBinder(binder, Key.get(type));
  }

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with no binding annotation.
   */
  public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type) {
    return newSetBinder(binder, Key.get(type));
  }

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with {@code annotation}.
   */
  public static <T> Multibinder<T> newSetBinder(
      Binder binder, TypeLiteral<T> type, Annotation annotation) {
    return newSetBinder(binder, Key.get(type, annotation));
  }

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with {@code annotation}.
   */
  public static <T> Multibinder<T> newSetBinder(
      Binder binder, Class<T> type, Annotation annotation) {
    return newSetBinder(binder, Key.get(type, annotation));
  }

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with {@code annotationType}.
   */
  public static <T> Multibinder<T> newSetBinder(
      Binder binder, TypeLiteral<T> type, Class<? extends Annotation> annotationType) {
    return newSetBinder(binder, Key.get(type, annotationType));
  }

  /**
   * Returns a new multibinder that collects instances of the key's type in a {@link Set} that is
   * itself bound with the annotation (if any) of the key.
   *
   * @since 4.0
   */
  public static <T> Multibinder<T> newSetBinder(Binder binder, Key<T> key) {
    return new Multibinder<T>(newRealSetBinder(binder.skipSources(Multibinder.class), key));
  }

  /**
   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
   * itself bound with {@code annotationType}.
   */
  public static <T> Multibinder<T> newSetBinder(
      Binder binder, Class<T> type, Class<? extends Annotation> annotationType) {
    return newSetBinder(binder, Key.get(type, annotationType));
  }

  private final RealMultibinder<T> delegate;

  private Multibinder(RealMultibinder<T> delegate) {
    this.delegate = delegate;
  }

  /**
   * Configures the bound set to silently discard duplicate elements. When multiple equal values are
   * bound, the one that gets included is arbitrary. When multiple modules contribute elements to
   * the set, this configuration option impacts all of them.
   *
   * @return this multibinder
   * @since 3.0
   */
  public Multibinder<T> permitDuplicates() {
    delegate.permitDuplicates();
    return this;
  }

  /**
   * Returns a binding builder used to add a new element in the set. Each bound element must have a
   * distinct value. Bound providers will be evaluated each time the set is injected.
   *
   *
It is an error to call this method without also calling one of the {@code to} methods on the
   * returned binding builder.
   *
   *
Scoping elements independently is supported. Use the {@code in} method to specify a binding
   * scope.
   */
  public LinkedBindingBuilder<T> addBinding() {
    return delegate.addBinding();
  }

  // Some tests rely on Multibinder implementing equals/hashCode

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

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


/*
 * Copyright (C) 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.assistedinject;

import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import java.lang.annotation.Annotation;

/**
 * Provides a factory that combines the caller's arguments with injector-supplied values to
 * construct objects.
 *
 *
Defining a factory

 *
 * Create an interface whose methods return the constructed type, or any of its supertypes. The
 * method's parameters are the arguments required to build the constructed type.
 *
 *
public interface PaymentFactory {
 *   Payment create(Date startDate, Money amount);
 * }

 *
 * You can name your factory methods whatever you like, such as create, createPayment
 * or newPayment.
 *
 *
Creating a type that accepts factory parameters

 *
 * {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject
 * Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor
 * should have parameters that match each of the factory method's parameters. Each factory-supplied
 * parameter requires an {@literal @}{@link Assisted} annotation. This serves to document that the
 * parameter is not bound by your application's modules.
 *
 *
public class RealPayment implements Payment {
 *   {@literal @}Inject
 *   public RealPayment(
 *      CreditService creditService,
 *      AuthService authService,
 *      {@literal @}Assisted Date startDate,
 *      {@literal @}Assisted Money amount) {
 *     ...
 *   }
 * }

 *
 *
Multiple factory methods for the same type

 *
 * If the factory contains many methods that return the same type, you can create multiple
 * constructors in your concrete class, each constructor marked with with {@literal @}{@link
 * AssistedInject}, in order to match the different parameters types of the factory methods.
 *
 *
public interface PaymentFactory {
 *    Payment create(Date startDate, Money amount);
 *    Payment createWithoutDate(Money amount);
 * }
 *
 * public class RealPayment implements Payment {
 *  {@literal @}AssistedInject
 *   public RealPayment(
 *      CreditService creditService,
 *      AuthService authService,
 *     {@literal @}Assisted Date startDate,
 *     {@literal @}Assisted Money amount) {
 *     ...
 *   }
 *
 *  {@literal @}AssistedInject
 *   public RealPayment(
 *      CreditService creditService,
 *      AuthService authService,
 *     {@literal @}Assisted Money amount) {
 *     ...
 *   }
 * }

 *
 *
Configuring simple factories

 *
 * In your {@link Module module}, install a {@code FactoryModuleBuilder} that creates the factory:
 *
 *
install(new FactoryModuleBuilder()
 *     .implement(Payment.class, RealPayment.class)
 *     .build(PaymentFactory.class));

 *
 * As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
 * factory cannot be used until the injector has been initialized.
 *
 *
Configuring complex factories

 *
 * Factories can create an arbitrary number of objects, one per each method. Each factory method can
 * be configured using .implement.
 *
 *
public interface OrderFactory {
 *    Payment create(Date startDate, Money amount);
 *    Shipment create(Customer customer, Item item);
 *    Receipt create(Payment payment, Shipment shipment);
 * }
 *
 * [...]
 *
 * install(new FactoryModuleBuilder()
 *     .implement(Payment.class, RealPayment.class)
 *     // excluding .implement for Shipment means the implementation class
 *     // will be 'Shipment' itself, which is legal if it's not an interface.
 *     .implement(Receipt.class, RealReceipt.class)
 *     .build(OrderFactory.class));

 *
 *

 *
 *
Using the factory

 *
 * Inject your factory into your application classes. When you use the factory, your arguments will
 * be combined with values from the injector to construct an instance.
 *
 *
public class PaymentAction {
 *   {@literal @}Inject private PaymentFactory paymentFactory;
 *
 *   public void doPayment(Money amount) {
 *     Payment payment = paymentFactory.create(new Date(), amount);
 *     payment.apply();
 *   }
 * }

 *
 *
Making parameter types distinct

 *
 * The types of the factory method's parameters must be distinct. To use multiple parameters of the
 * same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the parameters.
 * The names must be applied to the factory method's parameters:
 *
 *
public interface PaymentFactory {
 *   Payment create(
 *       {@literal @}Assisted("startDate") Date startDate,
 *       {@literal @}Assisted("dueDate") Date dueDate,
 *       Money amount);
 * }

 *
 * ...and to the concrete type's constructor parameters:
 *
 *
public class RealPayment implements Payment {
 *   {@literal @}Inject
 *   public RealPayment(
 *      CreditService creditService,
 *      AuthService authService,
 *      {@literal @}Assisted("startDate") Date startDate,
 *      {@literal @}Assisted("dueDate") Date dueDate,
 *      {@literal @}Assisted Money amount) {
 *     ...
 *   }
 * }

 *
 *
Values are created by Guice

 *
 * Returned factories use child injectors to create values. The values are eligible for method
 * interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
 * returned.
 *
 *
More configuration options

 *
 * In addition to simply specifying an implementation class for any returned type, factories' return
 * values can be automatic or can be configured to use annotations:
 *
 *
If you just want to return the types specified in the factory, do not configure any
 * implementations:
 *
 *
public interface FruitFactory {
 *   Apple getApple(Color color);
 * }
 * ...
 * protected void configure() {
 *   install(new FactoryModuleBuilder().build(FruitFactory.class));
 * }

 *
 * Note that any type returned by the factory in this manner needs to be an implementation class.
 *
 *
To return two different implementations for the same interface from your factory, use binding
 * annotations on your return types:
 *
 *
interface CarFactory {
 *   {@literal @}Named("fast") Car getFastCar(Color color);
 *   {@literal @}Named("clean") Car getCleanCar(Color color);
 * }
 * ...
 * protected void configure() {
 *   install(new FactoryModuleBuilder()
 *       .implement(Car.class, Names.named("fast"), Porsche.class)
 *       .implement(Car.class, Names.named("clean"), Prius.class)
 *       .build(CarFactory.class));
 * }

 *
 *
Implementation limitations

 *
 * As a limitation of the implementation, it is prohibited to declare a factory method that accepts
 * a {@code Provider} as one of its arguments.
 *
 * @since 3.0
 * @author schmitt@google.com (Peter Schmitt)
 */
public final class FactoryModuleBuilder {

  private final BindingCollector bindings = new BindingCollector();

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) {
    return implement(source, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) {
    return implement(TypeLiteral.get(source), target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) {
    return implement(source, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      TypeLiteral<T> source, TypeLiteral<? extends T> target) {
    return implement(Key.get(source), target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      Class<T> source, Annotation annotation, Class<? extends T> target) {
    return implement(source, annotation, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      Class<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
    return implement(TypeLiteral.get(source), annotation, target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      TypeLiteral<T> source, Annotation annotation, Class<? extends T> target) {
    return implement(source, annotation, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      TypeLiteral<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
    return implement(Key.get(source, annotation), target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      Class<T> source, Class<? extends Annotation> annotationType, Class<? extends T> target) {
    return implement(source, annotationType, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      Class<T> source,
      Class<? extends Annotation> annotationType,
      TypeLiteral<? extends T> target) {
    return implement(TypeLiteral.get(source), annotationType, target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      TypeLiteral<T> source,
      Class<? extends Annotation> annotationType,
      Class<? extends T> target) {
    return implement(source, annotationType, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(
      TypeLiteral<T> source,
      Class<? extends Annotation> annotationType,
      TypeLiteral<? extends T> target) {
    return implement(Key.get(source, annotationType), target);
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) {
    return implement(source, TypeLiteral.get(target));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <T> FactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) {
    bindings.addBinding(source, target);
    return this;
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <F> Module build(Class<F> factoryInterface) {
    return build(TypeLiteral.get(factoryInterface));
  }

  /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
  public <F> Module build(TypeLiteral<F> factoryInterface) {
    return build(Key.get(factoryInterface));
  }

  public <F> Module build(final Key<F> factoryInterface) {
    return new AbstractModule() {
      @Override
      protected void configure() {
        Provider<F> provider = new FactoryProvider2<>(factoryInterface, bindings);
        binder().skipSources(this.getClass()).bind(factoryInterface).toProvider(provider);
      }
    };
  }
}


/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.internal;

import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.Scopes.SINGLETON;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.Stage;
import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProvisionListenerBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.util.List;
import java.util.logging.Logger;

/**
 * A partially-initialized injector. See {@link InternalInjectorCreator}, which uses this to build a
 * tree of injectors in batch.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
final class InjectorShell {

  private final List<Element> elements;
  private final InjectorImpl injector;

  private InjectorShell(List<Element> elements, InjectorImpl injector) {
    this.elements = elements;
    this.injector = injector;
  }

  InjectorImpl getInjector() {
    return injector;
  }

  List<Element> getElements() {
    return elements;
  }

  static class Builder {
    private final List<Element> elements = Lists.newArrayList();
    private final List<Module> modules = Lists.newArrayList();

    /** lazily constructed */
    private State state;

    private InjectorImpl parent;
    private InjectorOptions options;
    private Stage stage;

    /** null unless this exists in a {@link Binder#newPrivateBinder private environment} */
    private PrivateElementsImpl privateElements;

    Builder stage(Stage stage) {
      this.stage = stage;
      return this;
    }

    Builder parent(InjectorImpl parent) {
      this.parent = parent;
      this.state = new InheritingState(parent.state);
      this.options = parent.options;
      this.stage = options.stage;
      return this;
    }

    Builder privateElements(PrivateElements privateElements) {
      this.privateElements = (PrivateElementsImpl) privateElements;
      this.elements.addAll(privateElements.getElements());
      return this;
    }

    void addModules(Iterable<? extends Module> modules) {
      for (Module module : modules) {
        this.modules.add(module);
      }
    }

    Stage getStage() {
      return options.stage;
    }

    /** Synchronize on this before calling {@link #build}. */
    Object lock() {
      return getState().lock();
    }

    /**
     * Creates and returns the injector shells for the current modules. Multiple shells will be
     * returned if any modules contain {@link Binder#newPrivateBinder private environments}. The
     * primary injector will be first in the returned list.
     */
    List<InjectorShell> build(
        Initializer initializer,
        ProcessedBindingData bindingData,
        Stopwatch stopwatch,
        Errors errors) {
      checkState(stage != null, "Stage not initialized");
      checkState(privateElements == null || parent != null, "PrivateElements with no parent");
      checkState(state != null, "no state. Did you remember to lock() ?");

      // bind Singleton if this is a top-level injector
      if (parent == null) {
        modules.add(0, new RootModule());
      } else {
        modules.add(0, new InheritedScannersModule(parent.state));
      }
      elements.addAll(Elements.getElements(stage, modules));

      // Look for injector-changing options
      InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
      optionsProcessor.process(null, elements);
      options = optionsProcessor.getOptions(stage, options);

      InjectorImpl injector = new InjectorImpl(parent, state, options);
      if (privateElements != null) {
        privateElements.initInjector(injector);
      }

      // add default type converters if this is a top-level injector
      if (parent == null) {
        TypeConverterBindingProcessor.prepareBuiltInConverters(injector);
      }

      stopwatch.resetAndLog("Module execution");

      new MessageProcessor(errors).process(injector, elements);

      /*if[AOP]*/
      new InterceptorBindingProcessor(errors).process(injector, elements);
      stopwatch.resetAndLog("Interceptors creation");
      /*end[AOP]*/

      new ListenerBindingProcessor(errors).process(injector, elements);
      List<TypeListenerBinding> typeListenerBindings = injector.state.getTypeListenerBindings();
      injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings);
      List<ProvisionListenerBinding> provisionListenerBindings =
          injector.state.getProvisionListenerBindings();
      injector.provisionListenerStore =
          new ProvisionListenerCallbackStore(provisionListenerBindings);
      stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");

      new ScopeBindingProcessor(errors).process(injector, elements);
      stopwatch.resetAndLog("Scopes creation");

      new TypeConverterBindingProcessor(errors).process(injector, elements);
      stopwatch.resetAndLog("Converters creation");

      bindStage(injector, stage);
      bindInjector(injector);
      bindLogger(injector);

      // Process all normal bindings, then UntargettedBindings.
      // This is necessary because UntargettedBindings can create JIT bindings
      // and need all their other dependencies set up ahead of time.
      new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
      new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
      stopwatch.resetAndLog("Binding creation");

      new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
      stopwatch.resetAndLog("Module annotated method scanners creation");

      List<InjectorShell> injectorShells = Lists.newArrayList();
      injectorShells.add(new InjectorShell(elements, injector));

      // recursively build child shells
      PrivateElementProcessor processor = new PrivateElementProcessor(errors);
      processor.process(injector, elements);
      for (Builder builder : processor.getInjectorShellBuilders()) {
        injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors));
      }
      stopwatch.resetAndLog("Private environment creation");

      return injectorShells;
    }

    private State getState() {
      if (state == null) {
        state = new InheritingState(State.NONE);
      }
      return state;
    }
  }

  /**
   * The Injector is a special case because we allow both parent and child injectors to both have a
   * binding for that key.
   */
  private static void bindInjector(InjectorImpl injector) {
    Key<Injector> key = Key.get(Injector.class);
    InjectorFactory injectorFactory = new InjectorFactory(injector);
    injector.state.putBinding(
        key,
        new ProviderInstanceBindingImpl<Injector>(
            injector,
            key,
            SourceProvider.UNKNOWN_SOURCE,
            injectorFactory,
            Scoping.UNSCOPED,
            injectorFactory,
            ImmutableSet.<InjectionPoint>of()));
  }

  private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
    private final Injector injector;

    private InjectorFactory(Injector injector) {
      this.injector = injector;
    }

    @Override
    public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
      return injector;
    }

    @Override
    public Injector get() {
      return injector;
    }

    @Override
    public String toString() {
      return "Provider<Injector>";
    }
  }

  /**
   * The Logger is a special case because it knows the injection point of the injected member. It's
   * the only binding that does this.
   */
  private static void bindLogger(InjectorImpl injector) {
    Key<Logger> key = Key.get(Logger.class);
    LoggerFactory loggerFactory = new LoggerFactory();
    injector.state.putBinding(
        key,
        new ProviderInstanceBindingImpl<Logger>(
            injector,
            key,
            SourceProvider.UNKNOWN_SOURCE,
            loggerFactory,
            Scoping.UNSCOPED,
            loggerFactory,
            ImmutableSet.<InjectionPoint>of()));
  }

  private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
    @Override
    public Logger get(InternalContext context, Dependency<?> dependency, boolean linked) {
      InjectionPoint injectionPoint = dependency.getInjectionPoint();
      return injectionPoint == null
          ? Logger.getAnonymousLogger()
          : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
    }

    @Override
    public Logger get() {
      return Logger.getAnonymousLogger();
    }

    @Override
    public String toString() {
      return "Provider<Logger>";
    }
  }

  private static void bindStage(InjectorImpl injector, Stage stage) {
    Key<Stage> key = Key.get(Stage.class);
    InstanceBindingImpl<Stage> stageBinding =
        new InstanceBindingImpl<Stage>(
            injector,
            key,
            SourceProvider.UNKNOWN_SOURCE,
            new ConstantFactory<Stage>(Initializables.of(stage)),
            ImmutableSet.<InjectionPoint>of(),
            stage);
    injector.state.putBinding(key, stageBinding);
  }

  private static class RootModule implements Module {
    @Override
    public void configure(Binder binder) {
      binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
      binder.bindScope(Singleton.class, SINGLETON);
      binder.bindScope(javax.inject.Singleton.class, SINGLETON);
    }
  }

  private static class InheritedScannersModule implements Module {
    private final State state;

    InheritedScannersModule(State state) {
      this.state = state;
    }

    @Override
    public void configure(Binder binder) {
      for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
        binding.applyTo(binder);
      }
    }
  }
}


/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.spi;

import static com.google.inject.internal.MoreTypes.getRawType;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
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.Annotations;
import com.google.inject.internal.DeclaredMembers;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.Nullability;
import com.google.inject.internal.util.Classes;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A constructor, field or method that can receive injections. Typically this is a member with the
 * {@literal @}{@link Inject} annotation. For non-private, no argument constructors, the member may
 * omit the annotation.
 *
 * @author crazybob@google.com (Bob Lee)
 * @since 2.0
 */
public final class InjectionPoint {

  private static final Logger logger = Logger.getLogger(InjectionPoint.class.getName());

  private final boolean optional;
  private final Member member;
  private final TypeLiteral<?> declaringType;
  private final ImmutableList<Dependency<?>> dependencies;

  InjectionPoint(TypeLiteral<?> declaringType, Method method, boolean optional) {
    this.member = method;
    this.declaringType = declaringType;
    this.optional = optional;
    this.dependencies = forMember(method, declaringType, method.getParameterAnnotations());
  }

  InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
    this.member = constructor;
    this.declaringType = declaringType;
    this.optional = false;
    this.dependencies =
        forMember(constructor, declaringType, constructor.getParameterAnnotations());
  }

  InjectionPoint(TypeLiteral<?> declaringType, Field field, boolean optional) {
    this.member = field;
    this.declaringType = declaringType;
    this.optional = optional;

    Annotation[] annotations = field.getAnnotations();

    Errors errors = new Errors(field);
    Key<?> key = null;
    try {
      key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
    } catch (ConfigurationException e) {
      errors.merge(e.getErrorMessages());
    } catch (ErrorsException e) {
      errors.merge(e.getErrors());
    }
    errors.throwConfigurationExceptionIfErrorsExist();

    this.dependencies =
        ImmutableList.<Dependency<?>>of(
            newDependency(key, Nullability.allowsNull(annotations), -1));
  }

  private ImmutableList<Dependency<?>> forMember(
      Member member, TypeLiteral<?> type, Annotation[][] paramterAnnotations) {
    Errors errors = new Errors(member);

    List<Dependency<?>> dependencies = Lists.newArrayList();
    int index = 0;

    for (TypeLiteral<?> parameterType : type.getParameterTypes(member)) {
      try {
        Annotation[] parameterAnnotations = paramterAnnotations[index];
        Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
        dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
        index++;
      } catch (ConfigurationException e) {
        errors.merge(e.getErrorMessages());
      } catch (ErrorsException e) {
        errors.merge(e.getErrors());
      }
    }

    errors.throwConfigurationExceptionIfErrorsExist();
    return ImmutableList.copyOf(dependencies);
  }

  // This metohd is necessary to create a Dependency<T> with proper generic type information
  private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
    return new Dependency<T>(this, key, allowsNull, parameterIndex);
  }

  /** Returns the injected constructor, field, or method. */
  public Member getMember() {
    // TODO: Don't expose the original member (which probably has setAccessible(true)).
    return member;
  }

  /**
   * Returns the dependencies for this injection point. If the injection point is for a method or
   * constructor, the dependencies will correspond to that member's parameters. Field injection
   * points always have a single dependency for the field itself.
   *
   * @return a possibly-empty list
   */
  public List<Dependency<?>> getDependencies() {
    return dependencies;
  }

  /**
   * Returns true if this injection point shall be skipped if the injector cannot resolve bindings
   * for all required dependencies. Both explicit bindings (as specified in a module), and implicit
   * bindings ({@literal @}{@link com.google.inject.ImplementedBy ImplementedBy}, default
   * constructors etc.) may be used to satisfy optional injection points.
   */
  public boolean isOptional() {
    return optional;
  }

  /**
   * Returns true if the element is annotated with {@literal @}{@link Toolable}.
   *
   * @since 3.0
   */
  public boolean isToolable() {
    return ((AnnotatedElement) member).isAnnotationPresent(Toolable.class);
  }

  /**
   * Returns the generic type that defines this injection point. If the member exists on a
   * parameterized type, the result will include more type information than the member's {@link
   * Member#getDeclaringClass() raw declaring class}.
   *
   * @since 3.0
   */
  public TypeLiteral<?> getDeclaringType() {
    return declaringType;
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof InjectionPoint
        && member.equals(((InjectionPoint) o).member)
        && declaringType.equals(((InjectionPoint) o).declaringType);
  }

  @Override
  public int hashCode() {
    return member.hashCode() ^ declaringType.hashCode();
  }

  @Override
  public String toString() {
    return Classes.toString(member);
  }

  /**
   * Returns a new injection point for the specified constructor. If the declaring type of {@code
   * constructor} is parameterized (such as {@code List<T>}), prefer the overload that includes a
   * type literal.
   *
   * @param constructor any single constructor present on {@code type}.
   * @since 3.0
   */
  public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
    return new InjectionPoint(TypeLiteral.get(constructor.getDeclaringClass()), constructor);
  }

  /**
   * Returns a new injection point for the specified constructor of {@code type}.
   *
   * @param constructor any single constructor present on {@code type}.
   * @param type the concrete type that defines {@code constructor}.
   * @since 3.0
   */
  public static <T> InjectionPoint forConstructor(
      Constructor<T> constructor, TypeLiteral<? extends T> type) {
    if (type.getRawType() != constructor.getDeclaringClass()) {
      new Errors(type)
          .constructorNotDefinedByType(constructor, type)
          .throwConfigurationExceptionIfErrorsExist();
    }

    return new InjectionPoint(type, constructor);
  }

  /**
   * Returns a new injection point for the injectable constructor of {@code type}.
   *
   * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
   *     or a no-arguments constructor that is not private.
   * @throws ConfigurationException if there is no injectable constructor, more than one injectable
   *     constructor, or if parameters of the injectable constructor are malformed, such as a
   *     parameter with multiple binding annotations.
   */
  public static InjectionPoint forConstructorOf(TypeLiteral<?> type) {
    Class<?> rawType = getRawType(type.getType());
    Errors errors = new Errors(rawType);

    Constructor<?> injectableConstructor = null;
    for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {

      boolean optional;
      Inject guiceInject = constructor.getAnnotation(Inject.class);
      if (guiceInject == null) {
        javax.inject.Inject javaxInject = constructor.getAnnotation(javax.inject.Inject.class);
        if (javaxInject == null) {
          continue;
        }
        optional = false;
      } else {
        optional = guiceInject.optional();
      }

      if (optional) {
        errors.optionalConstructor(constructor);
      }

      if (injectableConstructor != null) {
        errors.tooManyConstructors(rawType);
      }

      injectableConstructor = constructor;
      checkForMisplacedBindingAnnotations(injectableConstructor, errors);
    }

    errors.throwConfigurationExceptionIfErrorsExist();

    if (injectableConstructor != null) {
      return new InjectionPoint(type, injectableConstructor);
    }

    // If no annotated constructor is found, look for a no-arg constructor instead.
    try {
      Constructor<?> noArgConstructor = rawType.getDeclaredConstructor();

      // Disallow private constructors on non-private classes (unless they have @Inject)
      if (Modifier.isPrivate(noArgConstructor.getModifiers())
          && !Modifier.isPrivate(rawType.getModifiers())) {
        errors.missingConstructor(rawType);
        throw new ConfigurationException(errors.getMessages());
      }

      checkForMisplacedBindingAnnotations(noArgConstructor, errors);
      return new InjectionPoint(type, noArgConstructor);
    } catch (NoSuchMethodException e) {
      errors.missingConstructor(rawType);
      throw new ConfigurationException(errors.getMessages());
    }
  }

  /**
   * Returns a new injection point for the injectable constructor of {@code type}.
   *
   * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
   *     or a no-arguments constructor that is not private.
   * @throws ConfigurationException if there is no injectable constructor, more than one injectable
   *     constructor, or if parameters of the injectable constructor are malformed, such as a
   *     parameter with multiple binding annotations.
   */
  public static InjectionPoint forConstructorOf(Class<?> type) {
    return forConstructorOf(TypeLiteral.get(type));
  }

  /**
   * Returns a new injection point for the specified method of {@code type}. This is useful for
   * extensions that need to build dependency graphs from arbitrary methods.
   *
   * @param method any single method present on {@code type}.
   * @param type the concrete type that defines {@code method}.
   * @since 4.0
   */
  public static <T> InjectionPoint forMethod(Method method, TypeLiteral<T> type) {
    return new InjectionPoint(type, method, false);
  }

  /**
   * Returns all static method and field injection points on {@code type}.
   *
   * @return a possibly empty set of injection points. The set has a specified iteration order. All
   *     fields are returned and then all methods. Within the fields, supertype fields are returned
   *     before subtype fields. Similarly, supertype methods are returned before subtype methods.
   * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
   *     a field with multiple binding annotations. The exception's {@link
   *     ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of
   *     the valid injection points.
   */
  public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral<?> type) {
    Errors errors = new Errors();

    Set<InjectionPoint> result;

    if (type.getRawType().isInterface()) {
      errors.staticInjectionOnInterface(type.getRawType());
      result = null;
    } else {
      result = getInjectionPoints(type, true, errors);
    }

    if (errors.hasErrors()) {
      throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
    }
    return result;
  }

  /**
   * Returns all static method and field injection points on {@code type}.
   *
   * @return a possibly empty set of injection points. The set has a specified iteration order. All
   *     fields are returned and then all methods. Within the fields, supertype fields are returned
   *     before subtype fields. Similarly, supertype methods are returned before subtype methods.
   * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
   *     a field with multiple binding annotations. The exception's {@link
   *     ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of
   *     the valid injection points.
   */
  public static Set<InjectionPoint> forStaticMethodsAndFields(Class<?> type) {
    return forStaticMethodsAndFields(TypeLiteral.get(type));
  }

  /**
   * Returns all instance method and field injection points on {@code type}.
   *
   * @return a possibly empty set of injection points. The set has a specified iteration order. All
   *     fields are returned and then all methods. Within the fields, supertype fields are returned
   *     before subtype fields. Similarly, supertype methods are returned before subtype methods.
   * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
   *     a field with multiple binding annotations. The exception's {@link
   *     ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of
   *     the valid injection points.
   */
  public static Set<InjectionPoint> forInstanceMethodsAndFields(TypeLiteral<?> type) {
    Errors errors = new Errors();
    Set<InjectionPoint> result = getInjectionPoints(type, false, errors);
    if (errors.hasErrors()) {
      throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
    }
    return result;
  }

  /**
   * Returns all instance method and field injection points on {@code type}.
   *
   * @return a possibly empty set of injection points. The set has a specified iteration order. All
   *     fields are returned and then all methods. Within the fields, supertype fields are returned
   *     before subtype fields. Similarly, supertype methods are returned before subtype methods.
   * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
   *     a field with multiple binding annotations. The exception's {@link
   *     ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of
   *     the valid injection points.
   */
  public static Set<InjectionPoint> forInstanceMethodsAndFields(Class<?> type) {
    return forInstanceMethodsAndFields(TypeLiteral.get(type));
  }

  /** Returns true if the binding annotation is in the wrong place. */
  private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) {
    Annotation misplacedBindingAnnotation =
        Annotations.findBindingAnnotation(
            errors, member, ((AnnotatedElement) member).getAnnotations());
    if (misplacedBindingAnnotation == null) {
      return false;
    }

    // don't warn about misplaced binding annotations on methods when there's a field with the same
    // name. In Scala, fields always get accessor methods (that we need to ignore). See bug 242.
    if (member instanceof Method) {
      try {
        if (member.getDeclaringClass().getDeclaredField(member.getName()) != null) {
          return false;
        }
      } catch (NoSuchFieldException ignore) {
      }
    }

    errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation);
    return true;
  }

  /** Node in the doubly-linked list of injectable members (fields and methods). */
  abstract static class InjectableMember {
    final TypeLiteral<?> declaringType;
    final boolean optional;
    final boolean jsr330;
    InjectableMember previous;
    InjectableMember next;

    InjectableMember(TypeLiteral<?> declaringType, Annotation atInject) {
      this.declaringType = declaringType;

      if (atInject.annotationType() == javax.inject.Inject.class) {
        optional = false;
        jsr330 = true;
        return;
      }

      jsr330 = false;
      optional = ((Inject) atInject).optional();
    }

    abstract InjectionPoint toInjectionPoint();
  }

  static class InjectableField extends InjectableMember {
    final Field field;

    InjectableField(TypeLiteral<?> declaringType, Field field, Annotation atInject) {
      super(declaringType, atInject);
      this.field = field;
    }

    @Override
    InjectionPoint toInjectionPoint() {
      return new InjectionPoint(declaringType, field, optional);
    }
  }

  static class InjectableMethod extends InjectableMember {
    final Method method;
    /**
     * true if this method overrode a method that was annotated with com.google.inject.Inject. used
     * to allow different override behavior for guice inject vs javax.inject.Inject
     */
    boolean overrodeGuiceInject;

    InjectableMethod(TypeLiteral<?> declaringType, Method method, Annotation atInject) {
      super(declaringType, atInject);
      this.method = method;
    }

    @Override
    InjectionPoint toInjectionPoint() {
      return new InjectionPoint(declaringType, method, optional);
    }

    public boolean isFinal() {
      return Modifier.isFinal(method.getModifiers());
    }
  }

  static Annotation getAtInject(AnnotatedElement member) {
    Annotation a = member.getAnnotation(javax.inject.Inject.class);
    return a == null ? member.getAnnotation(Inject.class) : a;
  }

  /** Linked list of injectable members. */
  static class InjectableMembers {
    InjectableMember head;
    InjectableMember tail;

    void add(InjectableMember member) {
      if (head == null) {
        head = tail = member;
      } else {
        member.previous = tail;
        tail.next = member;
        tail = member;
      }
    }

    void remove(InjectableMember member) {
      if (member.previous != null) {
        member.previous.next = member.next;
      }
      if (member.next != null) {
        member.next.previous = member.previous;
      }
      if (head == member) {
        head = member.next;
      }
      if (tail == member) {
        tail = member.previous;
      }
    }

    boolean isEmpty() {
      return head == null;
    }
  }

  /** Position in type hierarchy. */
  enum Position {
    TOP, // No need to check for overridden methods
    MIDDLE,
    BOTTOM // Methods won't be overridden
  }

  /**
   * Keeps track of injectable methods so we can remove methods that get overridden in O(1) time.
   * Uses our position in the type hierarchy to perform optimizations.
   */
  static class OverrideIndex {
    final InjectableMembers injectableMembers;
    Map<Signature, List<InjectableMethod>> bySignature;
    Position position = Position.TOP;

    OverrideIndex(InjectableMembers injectableMembers) {
      this.injectableMembers = injectableMembers;
    }

    /* Caches the signature for the last method. */
    Method lastMethod;
    Signature lastSignature;

    /**
     * Removes a method overridden by the given method, if present. In order to remain backwards
     * compatible with prior Guice versions, this will *not* remove overridden methods if
     * 'alwaysRemove' is false and the overridden signature was annotated with a
     * com.google.inject.Inject.
     *
     * @param method The method used to determine what is overridden and should be removed.
     * @param alwaysRemove true if overridden methods should be removed even if they were
     *     guice @Inject
     * @param injectableMethod if this method overrode any guice @Inject methods, {@link
     *     InjectableMethod#overrodeGuiceInject} is set to true
     */
    boolean removeIfOverriddenBy(
        Method method, boolean alwaysRemove, InjectableMethod injectableMethod) {
      if (position == Position.TOP) {
        // If we're at the top of the hierarchy, there's nothing to override.
        return false;
      }

      if (bySignature == null) {
        // We encountered a method in a subclass. Time to index the
        // methods in the parent class.
        bySignature = new HashMap<>();
        for (InjectableMember member = injectableMembers.head;
            member != null;
            member = member.next) {
          if (!(member instanceof InjectableMethod)) {
            continue;
          }
          InjectableMethod im = (InjectableMethod) member;
          if (im.isFinal()) {
            continue;
          }
          List<InjectableMethod> methods = new ArrayList<>();
          methods.add(im);
          bySignature.put(new Signature(im.method), methods);
        }
      }

      lastMethod = method;
      Signature signature = lastSignature = new Signature(method);
      List<InjectableMethod> methods = bySignature.get(signature);
      boolean removed = false;
      if (methods != null) {
        for (Iterator<InjectableMethod> iterator = methods.iterator(); iterator.hasNext(); ) {
          InjectableMethod possiblyOverridden = iterator.next();
          if (overrides(method, possiblyOverridden.method)) {
            boolean wasGuiceInject =
                !possiblyOverridden.jsr330 || possiblyOverridden.overrodeGuiceInject;
            if (injectableMethod != null) {
              injectableMethod.overrodeGuiceInject = wasGuiceInject;
            }
            // Only actually remove the methods if we want to force
            // remove or if the signature never specified @com.google.inject.Inject
            // somewhere.
            if (alwaysRemove || !wasGuiceInject) {
              removed = true;
              iterator.remove();
              injectableMembers.remove(possiblyOverridden);
            }
          }
        }
      }
      return removed;
    }

    /**
     * Adds the given method to the list of injection points. Keeps track of it in this index in
     * case it gets overridden.
     */
    void add(InjectableMethod injectableMethod) {
      injectableMembers.add(injectableMethod);
      if (position == Position.BOTTOM || injectableMethod.isFinal()) {
        // This method can't be overridden, so there's no need to index it.
        return;
      }
      if (bySignature != null) {
        // Try to reuse the signature we created during removal
        @SuppressWarnings("ReferenceEquality")
        Signature signature =
            injectableMethod.method == lastMethod
                ? lastSignature
                : new Signature(injectableMethod.method);
        List<InjectableMethod> methods =
            bySignature.computeIfAbsent(signature, k -> new ArrayList<>());
        methods.add(injectableMethod);
      }
    }
  }

  /**
   * Returns an ordered, immutable set of injection points for the given type. Members in
   * superclasses come before members in subclasses. Within a class, fields come before methods.
   * Overridden methods are filtered out. The order of fields/methods within a class is consistent
   * but undefined.
   *
   * @param statics true is this method should return static members, false for instance members
   * @param errors used to record errors
   */
  private static Set<InjectionPoint> getInjectionPoints(
      final TypeLiteral<?> type, boolean statics, Errors errors) {
    InjectableMembers injectableMembers = new InjectableMembers();
    OverrideIndex overrideIndex = null;

    List<TypeLiteral<?>> hierarchy = hierarchyFor(type);
    int topIndex = hierarchy.size() - 1;
    for (int i = topIndex; i >= 0; i--) {
      if (overrideIndex != null && i < topIndex) {
        // Knowing the position within the hierarchy helps us make optimizations.
        if (i == 0) {
          overrideIndex.position = Position.BOTTOM;
        } else {
          overrideIndex.position = Position.MIDDLE;
        }
      }

      TypeLiteral<?> current = hierarchy.get(i);

      for (Field field : getDeclaredFields(current)) {
        if (Modifier.isStatic(field.getModifiers()) == statics) {
          Annotation atInject = getAtInject(field);
          if (atInject != null) {
            InjectableField injectableField = new InjectableField(current, field, atInject);
            if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
              errors.cannotInjectFinalField(field);
            }
            injectableMembers.add(injectableField);
          }
        }
      }

      for (Method method : getDeclaredMethods(current)) {
        if (isEligibleForInjection(method, statics)) {
          Annotation atInject = getAtInject(method);
          if (atInject != null) {
            InjectableMethod injectableMethod = new InjectableMethod(current, method, atInject);
            if (checkForMisplacedBindingAnnotations(method, errors)
                || !isValidMethod(injectableMethod, errors)) {
              if (overrideIndex != null) {
                boolean removed =
                    overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
                if (removed) {
                  logger.log(
                      Level.WARNING,
                      "Method: {0} is not a valid injectable method ("
                          + "because it either has misplaced binding annotations "
                          + "or specifies type parameters) but is overriding a method that is "
                          + "valid. Because it is not valid, the method will not be injected. "
                          + "To fix this, make the method a valid injectable method.",
                      method);
                }
              }
              continue;
            }
            if (statics) {
              injectableMembers.add(injectableMethod);
            } else {
              if (overrideIndex == null) {
                /*
                 * Creating the override index lazily means that the first type in the hierarchy
                 * with injectable methods (not necessarily the top most type) will be treated as
                 * the TOP position and will enjoy the same optimizations (no checks for overridden
                 * methods, etc.).
                 */
                overrideIndex = new OverrideIndex(injectableMembers);
              } else {
                // Forcibly remove the overridden method, otherwise we'll inject
                // it twice.
                overrideIndex.removeIfOverriddenBy(method, true, injectableMethod);
              }
              overrideIndex.add(injectableMethod);
            }
          } else {
            if (overrideIndex != null) {
              boolean removed = overrideIndex.removeIfOverriddenBy(method, false, null);
              if (removed) {
                logger.log(
                    Level.WARNING,
                    "Method: {0} is not annotated with @Inject but "
                        + "is overriding a method that is annotated with @javax.inject.Inject."
                        + "Because it is not annotated with @Inject, the method will not be "
                        + "injected. To fix this, annotate the method with @Inject.",
                    method);
              }
            }
          }
        }
      }
    }

    if (injectableMembers.isEmpty()) {
      return Collections.emptySet();
    }

    ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
    for (InjectableMember im = injectableMembers.head; im != null; im = im.next) {
      try {
        builder.add(im.toInjectionPoint());
      } catch (ConfigurationException ignorable) {
        if (!im.optional) {
          errors.merge(ignorable.getErrorMessages());
        }
      }
    }
    return builder.build();
  }

  private static Field[] getDeclaredFields(TypeLiteral<?> type) {
    return DeclaredMembers.getDeclaredFields(type.getRawType());
  }

  private static Method[] getDeclaredMethods(TypeLiteral<?> type) {
    return DeclaredMembers.getDeclaredMethods(type.getRawType());
  }

  /**
   * Returns true if the method is eligible to be injected. This is different than {@link
   * #isValidMethod}, because ineligibility will not drop a method from being injected if a
   * superclass was eligible & valid. Bridge & synthetic methods are excluded from eligibility for
   * two reasons:
   *
   * <p>Prior to Java8, javac would generate these methods in subclasses without annotations, which
   * means this would accidentally stop injecting a method annotated with {@link
   * javax.inject.Inject}, since the spec says to stop injecting if a subclass isn't annotated with
   * it.
   *
   * <p>Starting at Java8, javac copies the annotations to the generated subclass method, except it
   * leaves out the generic types. If this considered it a valid injectable method, this would eject
   * the parent's overridden method that had the proper generic types, and would use invalid
   * injectable parameters as a result.
   *
   * <p>The fix for both is simply to ignore these synthetic bridge methods.
   */
  private static boolean isEligibleForInjection(Method method, boolean statics) {
    return Modifier.isStatic(method.getModifiers()) == statics
        && !method.isBridge()
        && !method.isSynthetic();
  }

  private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) {
    boolean result = true;
    if (injectableMethod.jsr330) {
      Method method = injectableMethod.method;
      if (Modifier.isAbstract(method.getModifiers())) {
        errors.cannotInjectAbstractMethod(method);
        result = false;
      }
      if (method.getTypeParameters().length > 0) {
        errors.cannotInjectMethodWithTypeParameters(method);
        result = false;
      }
    }
    return result;
  }

  private static List<TypeLiteral<?>> hierarchyFor(TypeLiteral<?> type) {
    List<TypeLiteral<?>> hierarchy = new ArrayList<>();
    TypeLiteral<?> current = type;
    while (current.getRawType() != Object.class) {
      hierarchy.add(current);
      current = current.getSupertype(current.getRawType().getSuperclass());
    }
    return hierarchy;
  }

  /**
   * Returns true if a overrides b. Assumes signatures of a and b are the same and a's declaring
   * class is a subclass of b's declaring class.
   */
  private static boolean overrides(Method a, Method b) {
    // See JLS section 8.4.8.1
    int modifiers = b.getModifiers();
    if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
      return true;
    }
    if (Modifier.isPrivate(modifiers)) {
      return false;
    }
    // b must be package-private
    return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
  }

  /** A method signature. Used to handle method overridding. */
  static class Signature {

    final String name;
    final Class[] parameterTypes;
    final int hash;

    Signature(Method method) {
      this.name = method.getName();
      this.parameterTypes = method.getParameterTypes();

      int h = name.hashCode();
      h = h * 31 + parameterTypes.length;
      for (Class parameterType : parameterTypes) {
        h = h * 31 + parameterType.hashCode();
      }
      this.hash = h;
    }

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

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof Signature)) {
        return false;
      }

      Signature other = (Signature) o;
      if (!name.equals(other.name)) {
        return false;
      }

      if (parameterTypes.length != other.parameterTypes.length) {
        return false;
      }

      for (int i = 0; i < parameterTypes.length; i++) {
        if (parameterTypes[i] != other.parameterTypes[i]) {
          return false;
        }
      }

      return true;
    }
  }
}


/*
 * Copyright (C) 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.spi;

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

import com.google.common.base.Objects;
import com.google.inject.Binder;
import com.google.inject.ConfigurationException;
import com.google.inject.MembersInjector;
import com.google.inject.TypeLiteral;
import java.util.Set;

/**
 * A lookup of the members injector for a type. Lookups are created explicitly in a module using
 * {@link com.google.inject.Binder#getMembersInjector(Class) getMembersInjector()} statements:
 *
 *

 *     MembersInjector<PaymentService> membersInjector
 *         = getMembersInjector(PaymentService.class);

 *
 * @author crazybob@google.com (Bob Lee)
 * @since 2.0
 */
public final class MembersInjectorLookup<T> implements Element {

  private final Object source;
  private final TypeLiteral<T> type;
  private MembersInjector<T> delegate;

  public MembersInjectorLookup(Object source, TypeLiteral<T> type) {
    this.source = checkNotNull(source, "source");
    this.type = checkNotNull(type, "type");
  }

  @Override
  public Object getSource() {
    return source;
  }

  /** Gets the type containing the members to be injected. */
  public TypeLiteral<T> getType() {
    return type;
  }

  @Override
  public <T> T acceptVisitor(ElementVisitor<T> visitor) {
    return visitor.visit(this);
  }

  /**
   * Sets the actual members injector.
   *
   * @throws IllegalStateException if the delegate is already set
   */
  public void initializeDelegate(MembersInjector<T> delegate) {
    checkState(this.delegate == null, "delegate already initialized");
    this.delegate = checkNotNull(delegate, "delegate");
  }

  @Override
  public void applyTo(Binder binder) {
    initializeDelegate(binder.withSource(getSource()).getMembersInjector(type));
  }

  /**
   * Returns the delegate members injector, or {@code null} if it has not yet been initialized. The
   * delegate will be initialized when this element is processed, or otherwise used to create an
   * injector.
   */
  public MembersInjector<T> getDelegate() {
    return delegate;
  }

  /**
   * Returns the instance methods and fields that will be injected to fulfill this request.
   *
   * @return a possibly empty set of injection points. The set has a specified iteration order. All
   *     fields are returned and then all methods. Within the fields, supertype fields are returned
   *     before subtype fields. Similarly, supertype methods are returned before subtype methods.
   * @throws ConfigurationException if there is a malformed injection point on the class of {@code
   *     instance}, such as a field with multiple binding annotations. The exception's {@link
   *     ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>} of
   *     the valid injection points.
   */
  public Set<InjectionPoint> getInjectionPoints() throws ConfigurationException {
    return InjectionPoint.forInstanceMethodsAndFields(type);
  }

  /**
   * Returns the looked up members injector. The result is not valid until this lookup has been
   * initialized, which usually happens when the injector is created. The members injector will
   * throw an {@code IllegalStateException} if you try to use it beforehand.
   */
  public MembersInjector<T> getMembersInjector() {
    return new MembersInjector<T>() {
      @Override
      public void injectMembers(T instance) {
        MembersInjector<T> local = delegate;
        if (local == null) {
          throw new IllegalStateException(
              "This MembersInjector cannot be used until the Injector has been created.");
        }
        local.injectMembers(instance);
      }

      @Override
      public String toString() {
        return "MembersInjector<" + type + ">";
      }
    };
  }

  @Override
  public boolean equals(Object obj) {
    return obj instanceof MembersInjectorLookup
        && ((MembersInjectorLookup<?>) obj).type.equals(type)
        && ((MembersInjectorLookup<?>) obj).source.equals(source);
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(type, source);
  }
}


package com.google.inject.throwingproviders;

import static com.google.inject.throwingproviders.ProviderChecker.checkInterface;

import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;

/**
 * Static utility methods for creating and working with instances of {@link CheckedProvider}.
 *
 * @author eatnumber1@google.com (Russ Harmon)
 * @since 4.2
 */
public final class CheckedProviders {
  private abstract static class CheckedProviderInvocationHandler<T> implements InvocationHandler {
    private boolean isGetMethod(Method method) {
      // Since Java does not allow multiple methods with the same name and number & type of
      // arguments, this is all we need to check to see if it is an overriding method of
      // CheckedProvider#get().
      return method.getName().equals("get") && method.getParameterTypes().length == 0;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if (method.getDeclaringClass() == Object.class) {
        return method.invoke(this, args);
      } else if (isGetMethod(method)) {
        return invokeGet(proxy, method);
      }
      throw new UnsupportedOperationException(
          String.format(
              "Unsupported method <%s> with args <%s> invoked on <%s>",
              method, Arrays.toString(args), proxy));
    }

    protected abstract T invokeGet(Object proxy, Method method) throws Throwable;

    @Override
    public abstract String toString();
  }

  private static final class ReturningHandler<T> extends CheckedProviderInvocationHandler<T> {
    private final T returned;

    ReturningHandler(T returned) {
      this.returned = returned;
    }

    @Override
    protected T invokeGet(Object proxy, Method method) throws Throwable {
      return returned;
    }

    @Override
    public String toString() {
      return String.format("generated CheckedProvider returning <%s>", returned);
    }
  }

  private static final class ThrowingHandler extends CheckedProviderInvocationHandler<Object> {
    private final Constructor<? extends Throwable> throwableCtor;
    private final String typeName;

    private ThrowingHandler(Constructor<? extends Throwable> throwableCtor, String typeName) {
      this.throwableCtor = throwableCtor;
      this.typeName = typeName;

      this.throwableCtor.setAccessible(true);
    }

    static ThrowingHandler forClass(Class<? extends Throwable> throwable) {
      try {
        return new ThrowingHandler(throwable.getDeclaredConstructor(), throwable.getName());
      } catch (NoSuchMethodException e) {
        // This should have been caught by checkThrowable
        throw new AssertionError(
            String.format(
                "Throwable <%s> does not have a no-argument constructor", throwable.getName()));
      }
    }

    @Override
    protected Object invokeGet(Object proxy, Method method) throws Throwable {
      throw this.throwableCtor.newInstance();
    }

    @Override
    public String toString() {
      return String.format("generated CheckedProvider throwing <%s>", this.typeName);
    }
  }

  private CheckedProviders() {}

  private static <T, P extends CheckedProvider<? super T>> P generateProvider(
      Class
 providerType, Optional<T> value, InvocationHandler handler) {
    checkInterface(providerType, getClassOptional(value));
    Object proxy =
        Proxy.newProxyInstance(
            providerType.getClassLoader(), new Class<?>[] {providerType}, handler);
    @SuppressWarnings("unchecked") // guaranteed by the newProxyInstance API
    P proxyP = (P) proxy;
    return proxyP;
  }

  private static <T, P extends CheckedProvider<? super T>> P generateProvider(
      TypeLiteral
 providerType, Optional<T> value, InvocationHandler handler) {
    // TODO(eatnumber1): Understand why TypeLiteral#getRawType returns a Class<? super T> rather
    // than a Class<T> and remove this unsafe cast.
    Class
 providerRaw = (Class) providerType.getRawType();
    return generateProvider(providerRaw, value, handler);
  }

  private static Optional<Class<?>> getClassOptional(Optional<?> value) {
    if (!value.isPresent()) {
      return Optional.absent();
    }
    return Optional.<Class<?>>of(value.get().getClass());
  }

  /**
   * Returns a {@link CheckedProvider} which always provides {@code instance}.
   *
   *
The provider type passed as {@code providerType} must be an interface. Calls to methods
   * other than {@link CheckedProvider#get} will throw {@link UnsupportedOperationException}.
   *
   * @param providerType the type of the {@link CheckedProvider} to return
   * @param instance the instance that should always be provided
   */
  public static <T, P extends CheckedProvider<? super T>> P of(
      TypeLiteral
 providerType, @Nullable T instance) {
    return generateProvider(
        providerType, Optional.fromNullable(instance), new ReturningHandler<T>(instance));
  }

  /**
   * Returns a {@link CheckedProvider} which always provides {@code instance}.
   *
   * @param providerType the type of the {@link CheckedProvider} to return
   * @param instance the instance that should always be provided
   * @see #of(TypeLiteral, T)
   */
  public static <T, P extends CheckedProvider<? super T>> P of(
      Class
 providerType, @Nullable T instance) {
    return of(TypeLiteral.get(providerType), instance);
  }

  /**
   * Returns a {@link CheckedProvider} which always throws exceptions.
   *
   *
This method uses the nullary (no argument) constructor of {@code throwable} to create a new
   * instance of the given {@link Throwable} on each method invocation which is then thrown
   * immediately.
   *
   *
See {@link #of(TypeLiteral, T)} for more information.
   *
   * @param providerType the type of the {@link CheckedProvider} to return
   * @param throwable the type of the {@link Throwable} to throw
   * @see #of(TypeLiteral, T)
   */
  public static <T, P extends CheckedProvider<? super T>> P throwing(
      TypeLiteral
 providerType, Class<? extends Throwable> throwable) {
    // TODO(eatnumber1): Understand why TypeLiteral#getRawType returns a Class<? super T> rather
    // than a Class<T> and remove this unsafe cast.
    Class
 providerRaw = (Class) providerType.getRawType();
    checkThrowable(providerRaw, throwable);
    return generateProvider(
        providerType, Optional.<T>absent(), ThrowingHandler.forClass(throwable));
  }

  /**
   * Returns a {@link CheckedProvider} which always throws exceptions.
   *
   * @param providerType the type of the {@link CheckedProvider} to return
   * @param throwable the type of the {@link Throwable} to throw
   * @see #throwing(TypeLiteral, Class)
   */
  public static <T, P extends CheckedProvider<? super T>> P throwing(
      Class
 providerType, Class<? extends Throwable> throwable) {
    return throwing(TypeLiteral.get(providerType), throwable);
  }

  private static boolean isCheckedException(Class<? extends Throwable> thrownType) {
    return Exception.class.isAssignableFrom(thrownType)
        && !RuntimeException.class.isAssignableFrom(thrownType);
  }

  private static void checkThrowable(
      Class<? extends CheckedProvider<?>> providerType, Class<? extends Throwable> thrownType) {
    try {
      thrownType.getDeclaredConstructor();
    } catch (NoSuchMethodException e) {
      throw new IllegalArgumentException(
          String.format(
              "Thrown exception <%s> must have a no-argument constructor", thrownType.getName()),
          e);
    }

    if (!isCheckedException(thrownType)) {
      return;
    }

    Method getMethod;
    try {
      getMethod = providerType.getMethod("get");
    } catch (NoSuchMethodException e) {
      throw new IllegalArgumentException(
          String.format("Provider class <%s> must have a get() method", providerType.getName()), e);
    }

    @SuppressWarnings("unchecked") // guaranteed by getExceptionTypes
    List<Class<? extends Throwable>> exceptionTypes =
        Arrays.asList((Class<? extends Throwable>[]) getMethod.getExceptionTypes());

    if (exceptionTypes.size() == 0) {
      return;
    }

    // Check if the thrown exception is declared to be thrown in the method signature.
    for (Class<? extends Throwable> exceptionType : exceptionTypes) {
      if (exceptionType.isAssignableFrom(thrownType)) {
        return;
      }
    }

    throw new IllegalArgumentException(
        String.format(
            "Thrown exception <%s> is not declared to be thrown by <%s>",
            thrownType.getName(), getMethod));
  }
}


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

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.MoreTypes.canonicalize;

import com.google.common.collect.ImmutableList;
import com.google.inject.internal.MoreTypes;
import com.google.inject.util.Types;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.List;

/**
 * Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types,
 * so this class does. Forces clients to create a subclass of this class which enables retrieval of
 * the type information even at runtime.
 *
 *
For example, to create a type literal for {@code List<String>}, you can create an empty
 * anonymous inner class:
 *
 *
{@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
 *
 *
Along with modeling generic types, this class can resolve type parameters. For example, to
 * figure out what type {@code keySet()} returns on a {@code Map<Integer, String>}, use this code:
 *
 *
{@code
 * TypeLiteral<Map<Integer, String>> mapType
 *     = new TypeLiteral<Map<Integer, String>>() {};
 * TypeLiteral<?> keySetType
 *     = mapType.getReturnType(Map.class.getMethod("keySet"));
 * System.out.println(keySetType); // prints "Set<Integer>"
 * }

 *
 * @author crazybob@google.com (Bob Lee)
 * @author jessewilson@google.com (Jesse Wilson)
 */
public class TypeLiteral<T> {

  final Class<? super T> rawType;
  final Type type;
  final int hashCode;

  /**
   * Constructs a new type literal. Derives represented class from type parameter.
   *
   *
Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
   * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
   */
  @SuppressWarnings("unchecked")
  protected TypeLiteral() {
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
    this.hashCode = type.hashCode();
  }

  /** Unsafe. Constructs a type literal manually. */
  @SuppressWarnings("unchecked")
  TypeLiteral(Type type) {
    this.type = canonicalize(checkNotNull(type, "type"));
    this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
    this.hashCode = this.type.hashCode();
  }

  /**
   * Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
   * canonical form}.
   */
  static Type getSuperclassTypeParameter(Class<?> subclass) {
    Type superclass = subclass.getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    ParameterizedType parameterized = (ParameterizedType) superclass;
    return canonicalize(parameterized.getActualTypeArguments()[0]);
  }

  /** Gets type literal from super class's type parameter. */
  static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
    return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
  }

  /**
   * Returns the raw (non-generic) type for this type.
   *
   * @since 2.0
   */
  public final Class<? super T> getRawType() {
    return rawType;
  }

  /** Gets underlying {@code Type} instance. */
  public final Type getType() {
    return type;
  }

  /** Gets the type of this type's provider. */
  @SuppressWarnings("unchecked")
  final TypeLiteral<Provider<T>> providerType() {
    // This cast is safe and wouldn't generate a warning if Type had a type
    // parameter.
    return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
  }

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

  @Override
  public final boolean equals(Object o) {
    return o instanceof TypeLiteral<?> && MoreTypes.equals(type, ((TypeLiteral) o).type);
  }

  @Override
  public final String toString() {
    return MoreTypes.typeToString(type);
  }

  /** Gets type literal for the given {@code Type} instance. */
  public static TypeLiteral<?> get(Type type) {
    return new TypeLiteral<Object>(type);
  }

  /** Gets type literal for the given {@code Class} instance. */
  public static <T> TypeLiteral<T> get(Class<T> type) {
    return new TypeLiteral<T>(type);
  }

  /** Returns an immutable list of the resolved types. */
  private List<TypeLiteral<?>> resolveAll(Type[] types) {
    TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
    for (int t = 0; t < types.length; t++) {
      result[t] = resolve(types[t]);
    }
    return ImmutableList.copyOf(result);
  }

  /** Resolves known type parameters in {@code toResolve} and returns the result. */
  TypeLiteral<?> resolve(Type toResolve) {
    return TypeLiteral.get(resolveType(toResolve));
  }

  Type resolveType(Type toResolve) {
    // this implementation is made a little more complicated in an attempt to avoid object-creation
    while (true) {
      if (toResolve instanceof TypeVariable) {
        TypeVariable original = (TypeVariable) toResolve;
        toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
        if (toResolve == original) {
          return toResolve;
        }

      } else if (toResolve instanceof GenericArrayType) {
        GenericArrayType original = (GenericArrayType) toResolve;
        Type componentType = original.getGenericComponentType();
        Type newComponentType = resolveType(componentType);
        return componentType == newComponentType ? original : Types.arrayOf(newComponentType);

      } else if (toResolve instanceof ParameterizedType) {
        ParameterizedType original = (ParameterizedType) toResolve;
        Type ownerType = original.getOwnerType();
        Type newOwnerType = resolveType(ownerType);
        boolean changed = newOwnerType != ownerType;

        Type[] args = original.getActualTypeArguments();
        for (int t = 0, length = args.length; t < length; t++) {
          Type resolvedTypeArgument = resolveType(args[t]);
          if (resolvedTypeArgument != args[t]) {
            if (!changed) {
              args = args.clone();
              changed = true;
            }
            args[t] = resolvedTypeArgument;
          }
        }

        return changed
            ? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
            : original;

      } else if (toResolve instanceof WildcardType) {
        WildcardType original = (WildcardType) toResolve;
        Type[] originalLowerBound = original.getLowerBounds();
        Type[] originalUpperBound = original.getUpperBounds();

        if (originalLowerBound.length == 1) {
          Type lowerBound = resolveType(originalLowerBound[0]);
          if (lowerBound != originalLowerBound[0]) {
            return Types.supertypeOf(lowerBound);
          }
        } else if (originalUpperBound.length == 1) {
          Type upperBound = resolveType(originalUpperBound[0]);
          if (upperBound != originalUpperBound[0]) {
            return Types.subtypeOf(upperBound);
          }
        }
        return original;

      } else {
        return toResolve;
      }
    }
  }

  /**
   * Returns the generic form of {@code supertype}. For example, if this is {@code
   * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
   * Iterable.class}.
   *
   * @param supertype a superclass of, or interface implemented by, this.
   * @since 2.0
   */
  public TypeLiteral<?> getSupertype(Class<?> supertype) {
    checkArgument(
        supertype.isAssignableFrom(rawType), "%s is not a supertype of %s", supertype, this.type);
    return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
  }

  /**
   * Returns the resolved generic type of {@code field}.
   *
   * @param field a field defined by this or any superclass.
   * @since 2.0
   */
  public TypeLiteral<?> getFieldType(Field field) {
    checkArgument(
        field.getDeclaringClass().isAssignableFrom(rawType),
        "%s is not defined by a supertype of %s",
        field,
        type);
    return resolve(field.getGenericType());
  }

  /**
   * Returns the resolved generic parameter types of {@code methodOrConstructor}.
   *
   * @param methodOrConstructor a method or constructor defined by this or any supertype.
   * @since 2.0
   */
  public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
    Type[] genericParameterTypes;

    if (methodOrConstructor instanceof Method) {
      Method method = (Method) methodOrConstructor;
      checkArgument(
          method.getDeclaringClass().isAssignableFrom(rawType),
          "%s is not defined by a supertype of %s",
          method,
          type);
      genericParameterTypes = method.getGenericParameterTypes();

    } else if (methodOrConstructor instanceof Constructor) {
      Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
      checkArgument(
          constructor.getDeclaringClass().isAssignableFrom(rawType),
          "%s does not construct a supertype of %s",
          constructor,
          type);
      genericParameterTypes = constructor.getGenericParameterTypes();

    } else {
      throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
    }

    return resolveAll(genericParameterTypes);
  }

  /**
   * Returns the resolved generic exception types thrown by {@code constructor}.
   *
   * @param methodOrConstructor a method or constructor defined by this or any supertype.
   * @since 2.0
   */
  public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
    Type[] genericExceptionTypes;

    if (methodOrConstructor instanceof Method) {
      Method method = (Method) methodOrConstructor;
      checkArgument(
          method.getDeclaringClass().isAssignableFrom(rawType),
          "%s is not defined by a supertype of %s",
          method,
          type);
      genericExceptionTypes = method.getGenericExceptionTypes();

    } else if (methodOrConstructor instanceof Constructor) {
      Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
      checkArgument(
          constructor.getDeclaringClass().isAssignableFrom(rawType),
          "%s does not construct a supertype of %s",
          constructor,
          type);
      genericExceptionTypes = constructor.getGenericExceptionTypes();

    } else {
      throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
    }

    return resolveAll(genericExceptionTypes);
  }

  /**
   * Returns the resolved generic return type of {@code method}.
   *
   * @param method a method defined by this or any supertype.
   * @since 2.0
   */
  public TypeLiteral<?> getReturnType(Method method) {
    checkArgument(
        method.getDeclaringClass().isAssignableFrom(rawType),
        "%s is not defined by a supertype of %s",
        method,
        type);
    return resolve(method.getGenericReturnType());
  }
}