/*
 * 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.google.inject.ConfigurationException;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Types;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.NoSuchElementException;

/**
 * Static methods for working with types that we aren't publishing in the public {@code Types} API.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
public class MoreTypes {

  public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};

  private MoreTypes() {}

  private static final ImmutableMap<TypeLiteral<?>, TypeLiteral<?>> PRIMITIVE_TO_WRAPPER =
      new ImmutableMap.Builder<TypeLiteral<?>, TypeLiteral<?>>()
          .put(TypeLiteral.get(boolean.class), TypeLiteral.get(Boolean.class))
          .put(TypeLiteral.get(byte.class), TypeLiteral.get(Byte.class))
          .put(TypeLiteral.get(short.class), TypeLiteral.get(Short.class))
          .put(TypeLiteral.get(int.class), TypeLiteral.get(Integer.class))
          .put(TypeLiteral.get(long.class), TypeLiteral.get(Long.class))
          .put(TypeLiteral.get(float.class), TypeLiteral.get(Float.class))
          .put(TypeLiteral.get(double.class), TypeLiteral.get(Double.class))
          .put(TypeLiteral.get(char.class), TypeLiteral.get(Character.class))
          .put(TypeLiteral.get(void.class), TypeLiteral.get(Void.class))
          .build();

  /**
   * Returns a key that doesn't hold any references to parent classes. This is necessary for
   * anonymous keys, so ensure we don't hold a ref to the containing module (or class) forever.
   */
  public static <T> Key<T> canonicalizeKey(Key<T> key) {
    // If we know this isn't a subclass, return as-is.
    // Otherwise, recreate the key to avoid the subclass
    if (key.getClass() == Key.class) {
      return key;
    } else if (key.getAnnotation() != null) {
      return Key.get(key.getTypeLiteral(), key.getAnnotation());
    } else if (key.getAnnotationType() != null) {
      return Key.get(key.getTypeLiteral(), key.getAnnotationType());
    } else {
      return Key.get(key.getTypeLiteral());
    }
  }

  /**
   * Returns an type that's appropriate for use in a key.
   *
   *
If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider}, this returns a
   * {@code com.google.inject.Provider} with the same type parameters.
   *
   *
If the type is a primitive, the corresponding wrapper type will be returned.
   *
   * @throws ConfigurationException if {@code type} contains a type variable
   */
  public static <T> TypeLiteral<T> canonicalizeForKey(TypeLiteral<T> typeLiteral) {
    Type type = typeLiteral.getType();
    if (!isFullySpecified(type)) {
      Errors errors = new Errors().keyNotFullySpecified(typeLiteral);
      throw new ConfigurationException(errors.getMessages());
    }

    if (typeLiteral.getRawType() == javax.inject.Provider.class) {
      ParameterizedType parameterizedType = (ParameterizedType) type;

      // the following casts are generally unsafe, but com.google.inject.Provider extends
      // javax.inject.Provider and is covariant
      @SuppressWarnings("unchecked")
      TypeLiteral<T> guiceProviderType =
          (TypeLiteral<T>)
              TypeLiteral.get(Types.providerOf(parameterizedType.getActualTypeArguments()[0]));
      return guiceProviderType;
    }

    @SuppressWarnings("unchecked")
    TypeLiteral<T> wrappedPrimitives = (TypeLiteral<T>) PRIMITIVE_TO_WRAPPER.get(typeLiteral);
    if (wrappedPrimitives != null) {
      return wrappedPrimitives;
    }

    // If we know this isn't a subclass, return as-is.
    if (typeLiteral.getClass() == TypeLiteral.class) {
      return typeLiteral;
    }

    // recreate the TypeLiteral to avoid anonymous TypeLiterals from holding refs to their
    // surrounding classes.
    @SuppressWarnings("unchecked")
    TypeLiteral<T> recreated = (TypeLiteral<T>) TypeLiteral.get(typeLiteral.getType());
    return recreated;
  }

  /** Returns true if {@code type} is free from type variables. */
  private static boolean isFullySpecified(Type type) {
    if (type instanceof Class) {
      return true;

    } else if (type instanceof CompositeType) {
      return ((CompositeType) type).isFullySpecified();

    } else if (type instanceof TypeVariable) {
      return false;

    } else {
      return ((CompositeType) canonicalize(type)).isFullySpecified();
    }
  }

  /**
   * Returns a type that is functionally equal but not necessarily equal according to {@link
   * Object#equals(Object) Object.equals()}. The returned type is {@link Serializable}.
   */
  public static Type canonicalize(Type type) {
    if (type instanceof Class) {
      Class<?> c = (Class<?>) type;
      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;

    } else if (type instanceof CompositeType) {
      return type;

    } else if (type instanceof ParameterizedType) {
      ParameterizedType p = (ParameterizedType) type;
      return new ParameterizedTypeImpl(
          p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());

    } else if (type instanceof GenericArrayType) {
      GenericArrayType g = (GenericArrayType) type;
      return new GenericArrayTypeImpl(g.getGenericComponentType());

    } else if (type instanceof WildcardType) {
      WildcardType w = (WildcardType) type;
      return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());

    } else {
      // type is either serializable as-is or unsupported
      return type;
    }
  }

  public static Class<?> getRawType(Type type) {
    if (type instanceof Class<?>) {
      // type is a normal class.
      return (Class<?>) type;

    } else if (type instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) type;

      // I'm not exactly sure why getRawType() returns Type instead of Class.
      // Neal isn't either but suspects some pathological case related
      // to nested classes exists.
      Type rawType = parameterizedType.getRawType();
      checkArgument(
          rawType instanceof Class,
          "Expected a Class, but <%s> is of type %s",
          type,
          type.getClass().getName());
      return (Class<?>) rawType;

    } else if (type instanceof GenericArrayType) {
      Type componentType = ((GenericArrayType) type).getGenericComponentType();
      return Array.newInstance(getRawType(componentType), 0).getClass();

    } else if (type instanceof TypeVariable || type instanceof WildcardType) {
      // we could use the variable's bounds, but that'll won't work if there are multiple.
      // having a raw type that's more general than necessary is okay
      return Object.class;

    } else {
      throw new IllegalArgumentException(
          "Expected a Class, ParameterizedType, or "
              + "GenericArrayType, but <"
              + type
              + "> is of type "
              + type.getClass().getName());
    }
  }

  /** Returns true if {@code a} and {@code b} are equal. */
  public static boolean equals(Type a, Type b) {
    if (a == b) {
      // also handles (a == null && b == null)
      return true;

    } else if (a instanceof Class) {
      // Class already specifies equals().
      return a.equals(b);

    } else if (a instanceof ParameterizedType) {
      if (!(b instanceof ParameterizedType)) {
        return false;
      }

      // TODO: save a .clone() call
      ParameterizedType pa = (ParameterizedType) a;
      ParameterizedType pb = (ParameterizedType) b;
      return Objects.equal(pa.getOwnerType(), pb.getOwnerType())
          && pa.getRawType().equals(pb.getRawType())
          && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());

    } else if (a instanceof GenericArrayType) {
      if (!(b instanceof GenericArrayType)) {
        return false;
      }

      GenericArrayType ga = (GenericArrayType) a;
      GenericArrayType gb = (GenericArrayType) b;
      return equals(ga.getGenericComponentType(), gb.getGenericComponentType());

    } else if (a instanceof WildcardType) {
      if (!(b instanceof WildcardType)) {
        return false;
      }

      WildcardType wa = (WildcardType) a;
      WildcardType wb = (WildcardType) b;
      return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
          && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());

    } else if (a instanceof TypeVariable) {
      if (!(b instanceof TypeVariable)) {
        return false;
      }
      TypeVariable<?> va = (TypeVariable) a;
      TypeVariable<?> vb = (TypeVariable) b;
      return va.getGenericDeclaration().equals(vb.getGenericDeclaration())
          && va.getName().equals(vb.getName());

    } else {
      // This isn't a type we support. Could be a generic array type, wildcard type, etc.
      return false;
    }
  }

  private static int hashCodeOrZero(Object o) {
    return o != null ? o.hashCode() : 0;
  }

  public static String typeToString(Type type) {
    return type instanceof Class ? ((Class) type).getName() : type.toString();
  }

  /**
   * Returns the generic supertype for {@code type}. For example, given a class {@code IntegerSet},
   * the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the result when
   * the supertype is {@code Collection.class} is {@code Collection<Integer>}.
   */
  public static Type getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve) {
    if (toResolve == rawType) {
      return type;
    }

    // we skip searching through interfaces if unknown is an interface
    if (toResolve.isInterface()) {
      Class[] interfaces = rawType.getInterfaces();
      for (int i = 0, length = interfaces.length; i < length; i++) {
        if (interfaces[i] == toResolve) {
          return rawType.getGenericInterfaces()[i];
        } else if (toResolve.isAssignableFrom(interfaces[i])) {
          return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
        }
      }
    }

    // check our supertypes
    if (!rawType.isInterface()) {
      while (rawType != Object.class) {
        Class<?> rawSupertype = rawType.getSuperclass();
        if (rawSupertype == toResolve) {
          return rawType.getGenericSuperclass();
        } else if (toResolve.isAssignableFrom(rawSupertype)) {
          return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
        }
        rawType = rawSupertype;
      }
    }

    // we can't resolve this further
    return toResolve;
  }

  public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
    Class<?> declaredByRaw = declaringClassOf(unknown);

    // we can't reduce this further
    if (declaredByRaw == null) {
      return unknown;
    }

    Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw);
    if (declaredBy instanceof ParameterizedType) {
      int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
      return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
    }

    return unknown;
  }

  private static int indexOf(Object[] array, Object toFind) {
    for (int i = 0; i < array.length; i++) {
      if (toFind.equals(array[i])) {
        return i;
      }
    }
    throw new NoSuchElementException();
  }

  /**
   * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
   * a class.
   */
  private static Class<?> declaringClassOf(TypeVariable typeVariable) {
    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
    return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
  }

  public static class ParameterizedTypeImpl
      implements ParameterizedType, Serializable, CompositeType {
    private final Type ownerType;
    private final Type rawType;
    private final Type[] typeArguments;

    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
      // require an owner type if the raw type needs it
      ensureOwnerType(ownerType, rawType);

      this.ownerType = ownerType == null ? null : canonicalize(ownerType);
      this.rawType = canonicalize(rawType);
      this.typeArguments = typeArguments.clone();
      for (int t = 0; t < this.typeArguments.length; t++) {
        checkNotNull(this.typeArguments[t], "type parameter");
        checkNotPrimitive(this.typeArguments[t], "type parameters");
        this.typeArguments[t] = canonicalize(this.typeArguments[t]);
      }
    }

    @Override
    public Type[] getActualTypeArguments() {
      return typeArguments.clone();
    }

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

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

    @Override
    public boolean isFullySpecified() {
      if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
        return false;
      }

      if (!MoreTypes.isFullySpecified(rawType)) {
        return false;
      }

      for (Type type : typeArguments) {
        if (!MoreTypes.isFullySpecified(type)) {
          return false;
        }
      }

      return true;
    }

    @Override
    public boolean equals(Object other) {
      return other instanceof ParameterizedType
          && MoreTypes.equals(this, (ParameterizedType) other);
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
    }

    @Override
    public String toString() {
      StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
      stringBuilder.append(typeToString(rawType));

      if (typeArguments.length == 0) {
        return stringBuilder.toString();
      }

      stringBuilder.append("<").append(typeToString(typeArguments[0]));
      for (int i = 1; i < typeArguments.length; i++) {
        stringBuilder.append(", ").append(typeToString(typeArguments[i]));
      }
      return stringBuilder.append(">").toString();
    }

    private static void ensureOwnerType(Type ownerType, Type rawType) {
      if (rawType instanceof Class<?>) {
        Class rawTypeAsClass = (Class) rawType;
        checkArgument(
            ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
            "No owner type for enclosed %s",
            rawType);
        checkArgument(
            ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
            "Owner type for unenclosed %s",
            rawType);
      }
    }

    private static final long serialVersionUID = 0;
  }

  public static class GenericArrayTypeImpl
      implements GenericArrayType, Serializable, CompositeType {
    private final Type componentType;

    public GenericArrayTypeImpl(Type componentType) {
      this.componentType = canonicalize(componentType);
    }

    @Override
    public Type getGenericComponentType() {
      return componentType;
    }

    @Override
    public boolean isFullySpecified() {
      return MoreTypes.isFullySpecified(componentType);
    }

    @Override
    public boolean equals(Object o) {
      return o instanceof GenericArrayType && MoreTypes.equals(this, (GenericArrayType) o);
    }

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

    @Override
    public String toString() {
      return typeToString(componentType) + "[]";
    }

    private static final long serialVersionUID = 0;
  }

  /**
   * The WildcardType interface supports multiple upper bounds and multiple lower bounds. We only
   * support what the Java 6 language needs - at most one bound. If a lower bound is set, the upper
   * bound must be Object.class.
   */
  public static class WildcardTypeImpl implements WildcardType, Serializable, CompositeType {
    private final Type upperBound;
    private final Type lowerBound;

    public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
      checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound.");
      checkArgument(upperBounds.length == 1, "Must have exactly one upper bound.");

      if (lowerBounds.length == 1) {
        checkNotNull(lowerBounds[0], "lowerBound");
        checkNotPrimitive(lowerBounds[0], "wildcard bounds");
        checkArgument(upperBounds[0] == Object.class, "bounded both ways");
        this.lowerBound = canonicalize(lowerBounds[0]);
        this.upperBound = Object.class;

      } else {
        checkNotNull(upperBounds[0], "upperBound");
        checkNotPrimitive(upperBounds[0], "wildcard bounds");
        this.lowerBound = null;
        this.upperBound = canonicalize(upperBounds[0]);
      }
    }

    @Override
    public Type[] getUpperBounds() {
      return new Type[] {upperBound};
    }

    @Override
    public Type[] getLowerBounds() {
      return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
    }

    @Override
    public boolean isFullySpecified() {
      return MoreTypes.isFullySpecified(upperBound)
          && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));
    }

    @Override
    public boolean equals(Object other) {
      return other instanceof WildcardType && MoreTypes.equals(this, (WildcardType) other);
    }

    @Override
    public int hashCode() {
      // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
      return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
    }

    @Override
    public String toString() {
      if (lowerBound != null) {
        return "? super " + typeToString(lowerBound);
      } else if (upperBound == Object.class) {
        return "?";
      } else {
        return "? extends " + typeToString(upperBound);
      }
    }

    private static final long serialVersionUID = 0;
  }

  private static void checkNotPrimitive(Type type, String use) {
    checkArgument(
        !(type instanceof Class<?>) || !((Class) type).isPrimitive(),
        "Primitive types are not allowed in %s: %s",
        use,
        type);
  }

  /** A type formed from other types, such as arrays, parameterized types or wildcard types */
  private interface CompositeType {
    /** Returns true if there are no type variables in this type. */
    boolean isFullySpecified();
  }
}


/*
 * 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.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * A wrapping dispatcher for servlets, in much the same way as {@link ManagedFilterPipeline} is for
 * filters.
 *
 * @author dhanji@gmail.com (Dhanji R. Prasanna)
 */
@Singleton
class ManagedServletPipeline {
  private final ServletDefinition[] servletDefinitions;
  private static final TypeLiteral<ServletDefinition> SERVLET_DEFS =
      TypeLiteral.get(ServletDefinition.class);

  @Inject
  public ManagedServletPipeline(Injector injector) {
    this.servletDefinitions = collectServletDefinitions(injector);
  }

  boolean hasServletsMapped() {
    return servletDefinitions.length > 0;
  }

  /**
   * Introspects the injector and collects all instances of bound {@code List<ServletDefinition>}
   * into a master list.
   *
   * <p>We have a guarantee that {@link com.google.inject.Injector#getBindings()} returns a map that
   * preserves insertion order in entry-set iterators.
   */
  private ServletDefinition[] collectServletDefinitions(Injector injector) {
    List<ServletDefinition> servletDefinitions = Lists.newArrayList();
    for (Binding<ServletDefinition> entry : injector.findBindingsByType(SERVLET_DEFS)) {
      servletDefinitions.add(entry.getProvider().get());
    }

    // Copy to a fixed size array for speed.
    return servletDefinitions.toArray(new ServletDefinition[servletDefinitions.size()]);
  }

  public void init(ServletContext servletContext, Injector injector) throws ServletException {
    Set<HttpServlet> initializedSoFar = Sets.newIdentityHashSet();

    for (ServletDefinition servletDefinition : servletDefinitions) {
      servletDefinition.init(servletContext, injector, initializedSoFar);
    }
  }

  public boolean service(ServletRequest request, ServletResponse response)
      throws IOException, ServletException {

    //stop at the first matching servlet and service
    for (ServletDefinition servletDefinition : servletDefinitions) {
      if (servletDefinition.service(request, response)) {
        return true;
      }
    }

    //there was no match...
    return false;
  }

  public void destroy() {
    Set<HttpServlet> destroyedSoFar = Sets.newIdentityHashSet();
    for (ServletDefinition servletDefinition : servletDefinitions) {
      servletDefinition.destroy(destroyedSoFar);
    }
  }

  /**
   * @return Returns a request dispatcher wrapped with a servlet mapped to the given path or null if
   *     no mapping was found.
   */
  RequestDispatcher getRequestDispatcher(String path) {
    final String newRequestUri = path;

    // TODO(dhanji): check servlet spec to see if the following is legal or not.
    // Need to strip query string if requested...

    for (final ServletDefinition servletDefinition : servletDefinitions) {
      if (servletDefinition.shouldServe(path)) {
        return new RequestDispatcher() {
          @Override
          public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
              throws ServletException, IOException {
            Preconditions.checkState(
                !servletResponse.isCommitted(),
                "Response has been committed--you can only call forward before"
                    + " committing the response (hint: don't flush buffers)");

            // clear buffer before forwarding
            servletResponse.resetBuffer();

            ServletRequest requestToProcess;
            if (servletRequest instanceof HttpServletRequest) {
              requestToProcess = wrapRequest((HttpServletRequest) servletRequest, newRequestUri);
            } else {
              // This should never happen, but instead of throwing an exception
              // we will allow a happy case pass thru for maximum tolerance to
              // legacy (and internal) code.
              requestToProcess = servletRequest;
            }

            // now dispatch to the servlet
            doServiceImpl(servletDefinition, requestToProcess, servletResponse);
          }

          @Override
          public void include(ServletRequest servletRequest, ServletResponse servletResponse)
              throws ServletException, IOException {
            // route to the target servlet
            doServiceImpl(servletDefinition, servletRequest, servletResponse);
          }

          private void doServiceImpl(
              ServletDefinition servletDefinition,
              ServletRequest servletRequest,
              ServletResponse servletResponse)
              throws ServletException, IOException {
            servletRequest.setAttribute(REQUEST_DISPATCHER_REQUEST, Boolean.TRUE);

            try {
              servletDefinition.doService(servletRequest, servletResponse);
            } finally {
              servletRequest.removeAttribute(REQUEST_DISPATCHER_REQUEST);
            }
          }
        };
      }
    }

    //otherwise, can't process
    return null;
  }

  // visible for testing
  static HttpServletRequest wrapRequest(HttpServletRequest request, String newUri) {
    return new RequestDispatcherRequestWrapper(request, newUri);
  }

  /**
   * A Marker constant attribute that when present in the request indicates to Guice servlet that
   * this request has been generated by a request dispatcher rather than the servlet pipeline. In
   * accordance with section 8.4.2 of the Servlet 2.4 specification.
   */
  public static final String REQUEST_DISPATCHER_REQUEST = "javax.servlet.forward.servlet_path";

  private static class RequestDispatcherRequestWrapper extends HttpServletRequestWrapper {
    private final String newRequestUri;

    public RequestDispatcherRequestWrapper(
        HttpServletRequest servletRequest, String newRequestUri) {
      super(servletRequest);
      this.newRequestUri = newRequestUri;
    }

    @Override
    public String getRequestURI() {
      return newRequestUri;
    }

    @Override
    public StringBuffer getRequestURL() {
      StringBuffer url = new StringBuffer();
      String scheme = getScheme();
      int port = getServerPort();

      url.append(scheme);
      url.append("://");
      url.append(getServerName());
      // port might be -1 in some cases (see java.net.URL.getPort)
      if (port > 0
          && (("http".equals(scheme) && (port != 80))
              || ("https".equals(scheme) && (port != 443)))) {
        url.append(':');
        url.append(port);
      }
      url.append(getRequestURI());

      return (url);
    }
  }
}


/*
 * 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 com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.TypeConverter;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * Handles {@code Binder.convertToTypes} commands.
 *
 * @author crazybob@google.com (Bob Lee)
 * @author jessewilson@google.com (Jesse Wilson)
 */
final class TypeConverterBindingProcessor extends AbstractProcessor {

  TypeConverterBindingProcessor(Errors errors) {
    super(errors);
  }

  /** Installs default converters for primitives, enums, and class literals. */
  static void prepareBuiltInConverters(InjectorImpl injector) {
    // Configure type converters.
    convertToPrimitiveType(injector, int.class, Integer.class);
    convertToPrimitiveType(injector, long.class, Long.class);
    convertToPrimitiveType(injector, boolean.class, Boolean.class);
    convertToPrimitiveType(injector, byte.class, Byte.class);
    convertToPrimitiveType(injector, short.class, Short.class);
    convertToPrimitiveType(injector, float.class, Float.class);
    convertToPrimitiveType(injector, double.class, Double.class);

    convertToClass(
        injector,
        Character.class,
        new TypeConverter() {
          @Override
          public Object convert(String value, TypeLiteral<?> toType) {
            value = value.trim();
            if (value.length() != 1) {
              throw new RuntimeException("Length != 1.");
            }
            return value.charAt(0);
          }

          @Override
          public String toString() {
            return "TypeConverter<Character>";
          }
        });

    convertToClasses(
        injector,
        Matchers.subclassesOf(Enum.class),
        new TypeConverter() {
          @Override
          @SuppressWarnings("unchecked")
          public Object convert(String value, TypeLiteral<?> toType) {
            return Enum.valueOf((Class) toType.getRawType(), value);
          }

          @Override
          public String toString() {
            return "TypeConverter<E extends Enum<E>>";
          }
        });

    internalConvertToTypes(
        injector,
        new AbstractMatcher<TypeLiteral<?>>() {
          @Override
          public boolean matches(TypeLiteral<?> typeLiteral) {
            return typeLiteral.getRawType() == Class.class;
          }

          @Override
          public String toString() {
            return "Class<?>";
          }
        },
        new TypeConverter() {
          @Override
          @SuppressWarnings("unchecked")
          public Object convert(String value, TypeLiteral<?> toType) {
            try {
              return Class.forName(value);
            } catch (ClassNotFoundException e) {
              throw new RuntimeException(e.getMessage());
            }
          }

          @Override
          public String toString() {
            return "TypeConverter<Class<?>>";
          }
        });
  }

  private static <T> void convertToPrimitiveType(
      InjectorImpl injector, Class<T> primitiveType, final Class<T> wrapperType) {
    try {
      final Method parser =
          wrapperType.getMethod("parse" + capitalize(primitiveType.getName()), String.class);

      TypeConverter typeConverter =
          new TypeConverter() {
            @Override
            @SuppressWarnings("unchecked")
            public Object convert(String value, TypeLiteral<?> toType) {
              try {
                return parser.invoke(null, value);
              } catch (IllegalAccessException e) {
                throw new AssertionError(e);
              } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException().getMessage());
              }
            }

            @Override
            public String toString() {
              return "TypeConverter<" + wrapperType.getSimpleName() + ">";
            }
          };

      convertToClass(injector, wrapperType, typeConverter);
    } catch (NoSuchMethodException e) {
      throw new AssertionError(e);
    }
  }

  private static <T> void convertToClass(
      InjectorImpl injector, Class<T> type, TypeConverter converter) {
    convertToClasses(injector, Matchers.identicalTo(type), converter);
  }

  private static void convertToClasses(
      InjectorImpl injector, final Matcher<? super Class<?>> typeMatcher, TypeConverter converter) {
    internalConvertToTypes(
        injector,
        new AbstractMatcher<TypeLiteral<?>>() {
          @Override
          public boolean matches(TypeLiteral<?> typeLiteral) {
            Type type = typeLiteral.getType();
            if (!(type instanceof Class)) {
              return false;
            }
            Class<?> clazz = (Class<?>) type;
            return typeMatcher.matches(clazz);
          }

          @Override
          public String toString() {
            return typeMatcher.toString();
          }
        },
        converter);
  }

  private static void internalConvertToTypes(
      InjectorImpl injector, Matcher<? super TypeLiteral<?>> typeMatcher, TypeConverter converter) {
    injector.state.addConverter(
        new TypeConverterBinding(SourceProvider.UNKNOWN_SOURCE, typeMatcher, converter));
  }

  @Override
  public Boolean visit(TypeConverterBinding command) {
    injector.state.addConverter(
        new TypeConverterBinding(
            command.getSource(), command.getTypeMatcher(), command.getTypeConverter()));
    return true;
  }

  private static String capitalize(String s) {
    if (s.length() == 0) {
      return s;
    }
    char first = s.charAt(0);
    char capitalized = Character.toUpperCase(first);
    return (first == capitalized) ? s : capitalized + s.substring(1);
  }
}


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

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.inject.internal.InternalFlags.CustomClassLoadingOption;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Utility methods for runtime code generation and class loading. We use this stuff for {@link
 * net.sf.cglib.reflect.FastClass faster reflection}, {@link net.sf.cglib.proxy.Enhancer method
 * interceptors} and to proxy circular dependencies.
 *
 *
When loading classes, we need to be careful of:
 *
 *

 *
Memory leaks. Generated classes need to be garbage collected in long-lived
 *     applications. Once an injector and any instances it created can be garbage collected, the
 *     corresponding generated classes should be collectable.
 *
Visibility. Containers like OSGi use class loader boundaries to
 *     enforce modularity at runtime.
 *

 *
 *
For each generated class, there's multiple class loaders involved:
 *
 *

 *
The related class's class loader. Every generated class services exactly one
 *     user-supplied class. This class loader must be used to access members with protected and
 *     package visibility.
 *
Guice's class loader.
 *
Our bridge class loader. This is a child of the user's class loader. It
 *     selectively delegates to either the user's class loader (for user classes) or the Guice class
 *     loader (for internal classes that are used by the generated classes). This class loader that
 *     owns the classes generated by Guice.
 *

 *
 * @author mcculls@gmail.com (Stuart McCulloch)
 * @author jessewilson@google.com (Jesse Wilson)
 */
public final class BytecodeGen {

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

  static final ClassLoader GUICE_CLASS_LOADER = canonicalize(BytecodeGen.class.getClassLoader());

  // initialization-on-demand...
  private static class SystemBridgeHolder {
    static final BridgeClassLoader SYSTEM_BRIDGE = new BridgeClassLoader();
  }

  /** ie. "com.google.inject.internal" */
  static final String GUICE_INTERNAL_PACKAGE =
      BytecodeGen.class.getName().replaceFirst("\\.internal\\..*$", ".internal");

  /*if[AOP]*/
  /** either "net.sf.cglib", or "com.google.inject.internal.cglib" */
  static final String CGLIB_PACKAGE =
      net.sf.cglib.proxy.Enhancer.class.getName().replaceFirst("\\.cglib\\..*$", ".cglib");

  static final net.sf.cglib.core.NamingPolicy FASTCLASS_NAMING_POLICY =
      new net.sf.cglib.core.DefaultNamingPolicy() {
        @Override
        protected String getTag() {
          return "ByGuice";
        }

        @Override
        public String getClassName(
            String prefix, String source, Object key, net.sf.cglib.core.Predicate names) {
          // we explicitly set the source here to "FastClass" so that our jarjar renaming
          // to $FastClass doesn't leak into the class names.  if we did not do this,
          // classes would end up looking like $$$FastClassByGuice$$, with the extra $
          // at the front.
          return super.getClassName(prefix, "FastClass", key, names);
        }
      };

  static final net.sf.cglib.core.NamingPolicy ENHANCER_NAMING_POLICY =
      new net.sf.cglib.core.DefaultNamingPolicy() {
        @Override
        protected String getTag() {
          return "ByGuice";
        }

        @Override
        public String getClassName(
            String prefix, String source, Object key, net.sf.cglib.core.Predicate names) {
          // we explicitly set the source here to "Enhancer" so that our jarjar renaming
          // to $Enhancer doesn't leak into the class names.  if we did not do this,
          // classes would end up looking like $$$EnhancerByGuice$$, with the extra $
          // at the front.
          return super.getClassName(prefix, "Enhancer", key, names);
        }
      };
  /*end[AOP]*/
  /*if[NO_AOP]
  private static final String CGLIB_PACKAGE = " "; // any string that's illegal in a package name
  end[NO_AOP]*/

  /**
   * Weak cache of bridge class loaders that make the Guice implementation classes visible to
   * various code-generated proxies of client classes.
   */
  private static final LoadingCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;

  static {
    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().weakKeys().weakValues();
    if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) {
      builder.maximumSize(0);
    }
    CLASS_LOADER_CACHE =
        builder.build(
            new CacheLoader<ClassLoader, ClassLoader>() {
              @Override
              public ClassLoader load(final ClassLoader typeClassLoader) {
                logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
                return AccessController.doPrivileged(
                    new PrivilegedAction<ClassLoader>() {
                      @Override
                      public ClassLoader run() {
                        return new BridgeClassLoader(typeClassLoader);
                      }
                    });
              }
            });
  }

  /**
   * Attempts to canonicalize null references to the system class loader. May return null if for
   * some reason the system loader is unavailable.
   */
  private static ClassLoader canonicalize(ClassLoader classLoader) {
    return classLoader != null ? classLoader : SystemBridgeHolder.SYSTEM_BRIDGE.getParent();
  }

  /** Returns the class loader to host generated classes for {@code type}. */
  public static ClassLoader getClassLoader(Class<?> type) {
    return getClassLoader(type, type.getClassLoader());
  }

  private static ClassLoader getClassLoader(Class<?> type, ClassLoader delegate) {

    // simple case: do nothing!
    if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) {
      return delegate;
    }

    // java.* types can be seen everywhere
    if (type.getName().startsWith("java.")) {
      return GUICE_CLASS_LOADER;
    }

    delegate = canonicalize(delegate);

    // no need for a bridge if using same class loader, or it's already a bridge
    if (delegate == GUICE_CLASS_LOADER || delegate instanceof BridgeClassLoader) {
      return delegate;
    }

    // don't try bridging private types as it won't work
    if (Visibility.forType(type) == Visibility.PUBLIC) {
      if (delegate != SystemBridgeHolder.SYSTEM_BRIDGE.getParent()) {
        // delegate guaranteed to be non-null here
        return CLASS_LOADER_CACHE.getUnchecked(delegate);
      }
      // delegate may or may not be null here
      return SystemBridgeHolder.SYSTEM_BRIDGE;
    }

    return delegate; // last-resort: do nothing!
  }

  /*if[AOP]*/
  // use fully-qualified names so imports don't need preprocessor statements
  /**
   * Returns a FastClass proxy for invoking the given member or {@code null} if access rules
   * disallow it.
   *
   * @see #newFastClassForMember(Class, Member) for a full description
   */
  public static net.sf.cglib.reflect.FastClass newFastClassForMember(Member member) {
    return newFastClassForMember(member.getDeclaringClass(), member);
  }

  /**
   * Returns a FastClass proxy for invoking the given member or {@code null} if access rules
   * disallow it.
   *
   *
FastClass works by generating a type in the same package as the target {@code type}. This
   * may or may not work depending on the access level of the class/member. It breaks down into the
   * following cases depending on accessibility:
   *
   *

   *
Public: This always works since we can generate the type into the {@link BridgeClassLoader}
   *     which ensures there are no versioning issues.
   *
Package private and Protected: This works as long as:
   *

   *
We can generate into the same classloader as the type. This is not possible for JDK
   *         types which use the 'bootstrap' loader.
   *
The classloader of the type has the same version of {@code FastClass} as we do. This
   *         may be violated when running in OSGI bundles.
   *

   *
   *
Private: This never works.
   *

   *
   * If we are unable to generate the type, then we return null and callers should work around by
   * using normal java reflection.
   */
  public static net.sf.cglib.reflect.FastClass newFastClassForMember(Class<?> type, Member member) {
    if (!new net.sf.cglib.core.VisibilityPredicate(type, false).evaluate(member)) {
      // the member cannot be indexed by fast class.  Bail out.
      return null;
    }

    boolean publiclyCallable = isPubliclyCallable(member);
    if (!publiclyCallable && !hasSameVersionOfCglib(type.getClassLoader())) {
      // The type is in a classloader with a different version of cglib and is not publicly visible
      // (so we can't use the bridge classloader to work around).  Bail out.
      return null;
    }
    net.sf.cglib.reflect.FastClass.Generator generator =
        new net.sf.cglib.reflect.FastClass.Generator();
    if (publiclyCallable) {
      // Use the bridge classloader if we can
      generator.setClassLoader(getClassLoader(type));
    }
    generator.setType(type);
    generator.setNamingPolicy(FASTCLASS_NAMING_POLICY);
    if (logger.isLoggable(Level.FINE)) {
      logger.fine("Loading " + type + " FastClass with " + generator.getClassLoader());
    }
    return generator.create();
  }

  /**
   * Returns true if the types classloader has the same version of cglib that BytecodeGen has. This
   * only returns false in strange OSGI situations, but it prevents us from using FastClass for non
   * public members.
   */
  private static boolean hasSameVersionOfCglib(ClassLoader classLoader) {
    Class<?> fc = net.sf.cglib.reflect.FastClass.class;
    try {
      return classLoader.loadClass(fc.getName()) == fc;
    } catch (ClassNotFoundException e) {
      return false;
    }
  }

  /**
   * Returns true if the member can be called by a fast class generated in a different classloader.
   */
  private static boolean isPubliclyCallable(Member member) {
    if (!Modifier.isPublic(member.getModifiers())) {
      return false;
    }
    Class<?>[] parameterTypes;
    if (member instanceof Constructor) {
      parameterTypes = ((Constructor) member).getParameterTypes();
    } else {
      Method method = (Method) member;
      if (!Modifier.isPublic(method.getReturnType().getModifiers())) {
        return false;
      }
      parameterTypes = method.getParameterTypes();
    }

    for (Class<?> type : parameterTypes) {
      if (!Modifier.isPublic(type.getModifiers())) {
        return false;
      }
    }
    return true;
  }

  public static net.sf.cglib.proxy.Enhancer newEnhancer(Class<?> type, Visibility visibility) {
    net.sf.cglib.proxy.Enhancer enhancer = new net.sf.cglib.proxy.Enhancer();
    enhancer.setSuperclass(type);
    enhancer.setUseFactory(false);
    if (visibility == Visibility.PUBLIC) {
      enhancer.setClassLoader(getClassLoader(type));
    }
    enhancer.setNamingPolicy(ENHANCER_NAMING_POLICY);
    logger.fine("Loading " + type + " Enhancer with " + enhancer.getClassLoader());
    return enhancer;
  }
  /*end[AOP]*/

  /**
   * The required visibility of a user's class from a Guice-generated class. Visibility of
   * package-private members depends on the loading classloader: only if two classes were loaded by
   * the same classloader can they see each other's package-private members. We need to be careful
   * when choosing which classloader to use for generated classes. We prefer our bridge classloader,
   * since it's OSGi-safe and doesn't leak permgen space. But often we cannot due to visibility.
   */
  public enum Visibility {

    /**
     * Indicates that Guice-generated classes only need to call and override public members of the
     * target class. These generated classes may be loaded by our bridge classloader.
     */
    PUBLIC {
      @Override
      public Visibility and(Visibility that) {
        return that;
      }
    },

    /**
     * Indicates that Guice-generated classes need to call or override package-private members.
     * These generated classes must be loaded in the same classloader as the target class. They
     * won't work with OSGi, and won't get garbage collected until the target class' classloader is
     * garbage collected.
     */
    SAME_PACKAGE {
      @Override
      public Visibility and(Visibility that) {
        return this;
      }
    };

    public static Visibility forMember(Member member) {
      if ((member.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
        return SAME_PACKAGE;
      }

      Class[] parameterTypes;
      if (member instanceof Constructor) {
        parameterTypes = ((Constructor) member).getParameterTypes();
      } else {
        Method method = (Method) member;
        if (forType(method.getReturnType()) == SAME_PACKAGE) {
          return SAME_PACKAGE;
        }
        parameterTypes = method.getParameterTypes();
      }

      for (Class<?> type : parameterTypes) {
        if (forType(type) == SAME_PACKAGE) {
          return SAME_PACKAGE;
        }
      }

      return PUBLIC;
    }

    public static Visibility forType(Class<?> type) {
      return (type.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0
          ? PUBLIC
          : SAME_PACKAGE;
    }

    public abstract Visibility and(Visibility that);
  }

  /**
   * Loader for Guice-generated classes. For referenced classes, this delegates to either either the
   * user's classloader (which is the parent of this classloader) or Guice's class loader.
   */
  private static class BridgeClassLoader extends ClassLoader {

    BridgeClassLoader() {
      // use system loader as parent
    }

    BridgeClassLoader(ClassLoader usersClassLoader) {
      super(usersClassLoader);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

      if (name.startsWith("sun.reflect") || name.startsWith("jdk.internal.reflect")) {
        // these reflection classes must be loaded from bootstrap class loader
        return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve);
      }

      if (name.startsWith(GUICE_INTERNAL_PACKAGE) || name.startsWith(CGLIB_PACKAGE)) {
        if (null == GUICE_CLASS_LOADER) {
          // use special system bridge to load classes from bootstrap class loader
          return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve);
        }
        try {
          Class<?> clazz = GUICE_CLASS_LOADER.loadClass(name);
          if (resolve) {
            resolveClass(clazz);
          }
          return clazz;
        } catch (Throwable e) {
          // fall-back to classic delegation
        }
      }

      return classicLoadClass(name, resolve);
    }

    // make the classic delegating loadClass method visible
    Class<?> classicLoadClass(String name, boolean resolve) throws ClassNotFoundException {
      return super.loadClass(name, resolve);
    }
  }
}


/*
 * 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 com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Exposed;
import com.google.inject.Key;
import com.google.inject.PrivateBinder;
import com.google.inject.Provides;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.ProvidesMethodBinding;
import com.google.inject.spi.ProvidesMethodTargetVisitor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;

/**
 * A provider that invokes a method and returns its result.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
public abstract class ProviderMethod<T> extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
    implements HasDependencies, ProvidesMethodBinding<T>, ProviderWithExtensionVisitor<T> {

  /**
   * Creates a {@link ProviderMethod}.
   *
   * <p>Unless {@code skipFastClassGeneration} is set, this will use {@link
   * net.sf.cglib.reflect.FastClass} to invoke the actual method, since it is significantly faster.
   * However, this will fail if the method is {@code private} or {@code protected}, since fastclass
   * is subject to java access policies.
   */
  static <T> ProviderMethod<T> create(
      Key<T> key,
      Method method,
      Object instance,
      ImmutableSet<Dependency<?>> dependencies,
      Class<? extends Annotation> scopeAnnotation,
      boolean skipFastClassGeneration,
      Annotation annotation) {
    int modifiers = method.getModifiers();
    /*if[AOP]*/
    if (!skipFastClassGeneration) {
      try {
        net.sf.cglib.reflect.FastClass fc = BytecodeGen.newFastClassForMember(method);
        if (fc != null) {
          return new FastClassProviderMethod<T>(
              key, fc, method, instance, dependencies, scopeAnnotation, annotation);
        }
      } catch (net.sf.cglib.core.CodeGenerationException e) {
        /* fall-through */
      }
    }
    /*end[AOP]*/

    if (!Modifier.isPublic(modifiers)
        || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
      method.setAccessible(true);
    }

    return new ReflectionProviderMethod<T>(
        key, method, instance, dependencies, scopeAnnotation, annotation);
  }

  protected final Object instance;
  protected final Method method;

  private final Key<T> key;
  private final Class<? extends Annotation> scopeAnnotation;
  private final ImmutableSet<Dependency<?>> dependencies;
  private final boolean exposed;
  private final Annotation annotation;

  /**
   * Set by {@link #initialize(InjectorImpl, Errors)} so it is always available prior to injection.
   */
  private SingleParameterInjector<?>[] parameterInjectors;

  /** @param method the method to invoke. It's return type must be the same type as {@code key}. */
  private ProviderMethod(
      Key<T> key,
      Method method,
      Object instance,
      ImmutableSet<Dependency<?>> dependencies,
      Class<? extends Annotation> scopeAnnotation,
      Annotation annotation) {
    // We can be safely initialized eagerly since our bindings must exist statically and it is an
    // error for them not to.
    super(InitializationTiming.EAGER);
    this.key = key;
    this.scopeAnnotation = scopeAnnotation;
    this.instance = instance;
    this.dependencies = dependencies;
    this.method = method;
    this.exposed = method.isAnnotationPresent(Exposed.class);
    this.annotation = annotation;
  }

  @Override
  public Key<T> getKey() {
    return key;
  }

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

  // exposed for GIN
  public Object getInstance() {
    return instance;
  }

  @Override
  public Object getEnclosingInstance() {
    return instance;
  }

  @Override
  public Annotation getAnnotation() {
    return annotation;
  }

  public void configure(Binder binder) {
    binder = binder.withSource(method);

    if (scopeAnnotation != null) {
      binder.bind(key).toProvider(this).in(scopeAnnotation);
    } else {
      binder.bind(key).toProvider(this);
    }

    if (exposed) {
      // the cast is safe 'cause the only binder we have implements PrivateBinder. If there's a
      // misplaced @Exposed, calling this will add an error to the binder's error queue
      ((PrivateBinder) binder).expose(key);
    }
  }

  @Override
  void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
    parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
  }

  @Override
  protected T doProvision(InternalContext context, Dependency<?> dependency)
      throws InternalProvisionException {
    try {
      T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
      if (t == null && !dependency.isNullable()) {
        InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
      }
      return t;
    } catch (IllegalAccessException e) {
      throw new AssertionError(e);
    } catch (InvocationTargetException userException) {
      Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
      throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
    }
  }

  /** Extension point for our subclasses to implement the provisioning strategy. */
  abstract T doProvision(Object[] parameters)
      throws IllegalAccessException, InvocationTargetException;

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

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

  @Override
  public String toString() {
    String annotationString = annotation.toString();
    // Show @Provides w/o the com.google.inject prefix.
    if (annotation.annotationType() == Provides.class) {
      annotationString = "@Provides";
    } else if (annotationString.endsWith("()")) {
      // Remove the common "()" suffix if there are no values.
      annotationString = annotationString.substring(0, annotationString.length() - 2);
    }
    return annotationString + " " + StackTraceElements.forMember(method);
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof ProviderMethod) {
      ProviderMethod<?> o = (ProviderMethod<?>) obj;
      return method.equals(o.method)
          && instance.equals(o.instance)
          && annotation.equals(o.annotation);
    } else {
      return false;
    }
  }

  @Override
  public int hashCode() {
    // Avoid calling hashCode on 'instance', which is a user-object
    // that might not be expecting it.
    // (We need to call equals, so we do.  But we can avoid hashCode.)
    return Objects.hashCode(method, annotation);
  }

  /*if[AOP]*/
  /**
   * A {@link ProviderMethod} implementation that uses {@link net.sf.cglib.reflect.FastClass#invoke}
   * to invoke the provider method.
   */
  private static final class FastClassProviderMethod<T> extends ProviderMethod<T> {
    final net.sf.cglib.reflect.FastClass fastClass;
    final int methodIndex;

    FastClassProviderMethod(
        Key<T> key,
        net.sf.cglib.reflect.FastClass fc,
        Method method,
        Object instance,
        ImmutableSet<Dependency<?>> dependencies,
        Class<? extends Annotation> scopeAnnotation,
        Annotation annotation) {
      super(key, method, instance, dependencies, scopeAnnotation, annotation);
      this.fastClass = fc;
      this.methodIndex = fc.getMethod(method).getIndex();
    }

    @SuppressWarnings("unchecked")
    @Override
    public T doProvision(Object[] parameters)
        throws IllegalAccessException, InvocationTargetException {
      return (T) fastClass.invoke(methodIndex, instance, parameters);
    }
  }
  /*end[AOP]*/

  /**
   * A {@link ProviderMethod} implementation that invokes the method using normal java reflection.
   */
  private static final class ReflectionProviderMethod<T> extends ProviderMethod<T> {
    ReflectionProviderMethod(
        Key<T> key,
        Method method,
        Object instance,
        ImmutableSet<Dependency<?>> dependencies,
        Class<? extends Annotation> scopeAnnotation,
        Annotation annotation) {
      super(key, method, instance, dependencies, scopeAnnotation, annotation);
    }

    @SuppressWarnings("unchecked")
    @Override
    T doProvision(Object[] parameters) throws IllegalAccessException, InvocationTargetException {
      return (T) method.invoke(instance, parameters);
    }
  }
}


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

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

import com.google.inject.ConfigurationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.internal.Annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Models a method or constructor parameter.
 *
 * @author jmourits@google.com (Jerome Mourits)
 * @author jessewilson@google.com (Jesse Wilson)
 */
class Parameter {

  private final Type type;
  private final boolean isAssisted;
  private final Annotation bindingAnnotation;
  private final boolean isProvider;

  private volatile Provider<? extends Object> provider;

  public Parameter(Type type, Annotation[] annotations) {
    this.type = type;
    this.bindingAnnotation = getBindingAnnotation(annotations);
    this.isAssisted = hasAssistedAnnotation(annotations);
    this.isProvider = isProvider(type);
  }

  public boolean isProvidedByFactory() {
    return isAssisted;
  }

  public Type getType() {
    return type;
  }

  @Override
  public String toString() {
    StringBuilder result = new StringBuilder();
    if (isAssisted) {
      result.append("@Assisted ");
    }
    if (bindingAnnotation != null) {
      result.append(bindingAnnotation).append(" ");
    }
    return result.append(type).toString();
  }

  private boolean hasAssistedAnnotation(Annotation[] annotations) {
    for (Annotation annotation : annotations) {
      if (annotation.annotationType().equals(Assisted.class)) {
        return true;
      }
    }
    return false;
  }

  /** Returns the Guice {@link Key} for this parameter. */
  public Object getValue(Injector injector) {
    if (null == provider) {
      synchronized (this) {
        if (null == provider) {
          provider =
              isProvider
                  ? injector.getProvider(getBindingForType(getProvidedType(type)))
                  : injector.getProvider(getPrimaryBindingKey());
        }
      }
    }

    return isProvider ? provider : provider.get();
  }

  public boolean isBound(Injector injector) {
    return isBound(injector, getPrimaryBindingKey())
        || isBound(injector, fixAnnotations(getPrimaryBindingKey()));
  }

  private boolean isBound(Injector injector, Key<?> key) {
    // This method is particularly lame - we really need an API that can test
    // for any binding, implicit or explicit
    try {
      return injector.getBinding(key) != null;
    } catch (ConfigurationException e) {
      return false;
    }
  }

  /**
   * Replace annotation instances with annotation types, this is only appropriate for testing if a
   * key is bound and not for injecting.
   *
   * <p>See Guice bug 125, https://github.com/google/guice/issues/125
   */
  public Key<?> fixAnnotations(Key<?> key) {
    return key.getAnnotation() == null
        ? key
        : Key.get(key.getTypeLiteral(), key.getAnnotation().annotationType());
  }

  Key<?> getPrimaryBindingKey() {
    return isProvider ? getBindingForType(getProvidedType(type)) : getBindingForType(type);
  }

  private Type getProvidedType(Type type) {
    return ((ParameterizedType) type).getActualTypeArguments()[0];
  }

  private boolean isProvider(Type type) {
    return type instanceof ParameterizedType
        && ((ParameterizedType) type).getRawType() == Provider.class;
  }

  private Key<?> getBindingForType(Type type) {
    return bindingAnnotation != null ? Key.get(type, bindingAnnotation) : Key.get(type);
  }

  /**
   * Returns the unique binding annotation from the specified list, or {@code null} if there are
   * none.
   *
   * @throws IllegalStateException if multiple binding annotations exist.
   */
  private Annotation getBindingAnnotation(Annotation[] annotations) {
    Annotation bindingAnnotation = null;
    for (Annotation annotation : annotations) {
      if (Annotations.isBindingAnnotation(annotation.annotationType())) {
        checkArgument(
            bindingAnnotation == null,
            "Parameter has multiple binding annotations: %s and %s",
            bindingAnnotation,
            annotation);
        bindingAnnotation = annotation;
      }
    }
    return bindingAnnotation;
  }
}


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

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory;
import com.google.inject.spi.InjectionPoint;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;

/**
 * Manages and injects instances at injector-creation time. This is made more complicated by
 * instances that request other instances while they're being injected. We overcome this by using
 * {@link Initializable}, which attempts to perform injection before use.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
final class Initializer {

  /** Is set to true once {@link #validateOustandingInjections} is called. */
  private volatile boolean validationStarted = false;

  /**
   * Allows us to detect circular dependencies. It's only used during injectable reference
   * initialization. After initialization direct access through volatile field is used.
   */
  private final CycleDetectingLockFactory<Class<?>> cycleDetectingLockFactory =
      new CycleDetectingLockFactory<Class<?>>();

  /**
   * Instances that need injection during injector creation to a source that registered them. New
   * references added before {@link #validateOustandingInjections}. Cleared up in {@link
   * #injectAll}.
   */
  private final List<InjectableReference<?>> pendingInjections = Lists.newArrayList();

  /**
   * Map that guarantees that no instance would get two references. New references added before
   * {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
   */
  private final IdentityHashMap<Object, InjectableReference<?>> initializablesCache =
      Maps.newIdentityHashMap();

  /**
   * Registers an instance for member injection when that step is performed.
   *
   * @param instance an instance that optionally has members to be injected (each annotated
   *     with @Inject).
   * @param binding the binding that caused this initializable to be created, if it exists.
   * @param source the source location that this injection was requested
   */
  <T> Initializable<T> requestInjection(
      InjectorImpl injector,
      T instance,
      Binding<T> binding,
      Object source,
      Set<InjectionPoint> injectionPoints) {
    checkNotNull(source);
    Preconditions.checkState(
        !validationStarted, "Member injection could not be requested after validation is started");
    ProvisionListenerStackCallback<T> provisionCallback =
        binding == null ? null : injector.provisionListenerStore.get(binding);

    // short circuit if the object has no injections or listeners.
    if (instance == null
        || (injectionPoints.isEmpty()
            && !injector.membersInjectorStore.hasTypeListeners()
            && provisionCallback == null)) {
      return Initializables.of(instance);
    }

    if (initializablesCache.containsKey(instance)) {
      @SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
      Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
      return cached;
    }

    InjectableReference<T> injectableReference =
        new InjectableReference<T>(
            injector,
            instance,
            binding == null ? null : binding.getKey(),
            provisionCallback,
            source,
            cycleDetectingLockFactory.create(instance.getClass()));
    initializablesCache.put(instance, injectableReference);
    pendingInjections.add(injectableReference);
    return injectableReference;
  }

  /**
   * Prepares member injectors for all injected instances. This prompts Guice to do static analysis
   * on the injected instances.
   */
  void validateOustandingInjections(Errors errors) {
    validationStarted = true;
    initializablesCache.clear();
    for (InjectableReference<?> reference : pendingInjections) {
      try {
        reference.validate(errors);
      } catch (ErrorsException e) {
        errors.merge(e.getErrors());
      }
    }
  }

  /**
   * Performs creation-time injections on all objects that require it. Whenever fulfilling an
   * injection depends on another object that requires injection, we inject it first. If the two
   * instances are codependent (directly or transitively), ordering of injection is arbitrary.
   */
  void injectAll(final Errors errors) {
    Preconditions.checkState(validationStarted, "Validation should be done before injection");
    for (InjectableReference<?> reference : pendingInjections) {
      try {
        reference.get();
      } catch (InternalProvisionException ipe) {
        errors.merge(ipe);
      }
    }
    pendingInjections.clear();
  }

  private enum InjectableReferenceState {
    NEW,
    VALIDATED,
    INJECTING,
    READY
  }

  private static class InjectableReference<T> implements Initializable<T> {
    private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
    private volatile MembersInjectorImpl<T> membersInjector = null;

    private final InjectorImpl injector;
    private final T instance;
    private final Object source;
    private final Key<T> key;
    private final ProvisionListenerStackCallback<T> provisionCallback;
    private final CycleDetectingLock<?> lock;

    public InjectableReference(
        InjectorImpl injector,
        T instance,
        Key<T> key,
        ProvisionListenerStackCallback<T> provisionCallback,
        Object source,
        CycleDetectingLock<?> lock) {
      this.injector = injector;
      this.key = key; // possibly null!
      this.provisionCallback = provisionCallback; // possibly null!
      this.instance = checkNotNull(instance, "instance");
      this.source = checkNotNull(source, "source");
      this.lock = checkNotNull(lock, "lock");
    }

    public void validate(Errors errors) throws ErrorsException {
      @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
      TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
      membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
      Preconditions.checkNotNull(
          membersInjector,
          "No membersInjector available for instance: %s, from key: %s",
          instance,
          key);
      state = InjectableReferenceState.VALIDATED;
    }

    /**
     * Reentrant. If {@code instance} was registered for injection at injector-creation time, this
     * method will ensure that all its members have been injected before returning.
     */
    @Override
    public T get() throws InternalProvisionException {
      // skipping acquiring lock if initialization is already finished
      if (state == InjectableReferenceState.READY) {
        return instance;
      }

      // acquire lock for current binding to initialize an instance
      Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
      if (!lockCycle.isEmpty()) {
        // Potential deadlock detected and creation lock is not taken.
        // According to injectAll()'s contract return non-initialized instance.

        // This condition should not be possible under the current Guice implementation.
        // This clause exists for defensive programming purposes.

        // Reasoning:
        // get() is called either directly from injectAll(), holds no locks and can not create
        // a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
        // Before calling get() object has to be requested for injection.
        // Initializer.requestInjection() is called either for constant object bindings, which wrap
        // creation into a Singleton scope, or from Binder.requestInjection(), which
        // has to use Singleton scope to reuse the same InjectableReference to potentially
        // create a lock cycle.
        return instance;
      }
      try {
        // lock acquired, current thread owns this instance initialization
        switch (state) {
          case READY:
            return instance;
            // When instance depends on itself in the same thread potential dead lock
            // is not detected. We have to prevent a stack overflow and we use
            // an "injecting" stage to short-circuit a call.
          case INJECTING:
            return instance;
          case VALIDATED:
            state = InjectableReferenceState.INJECTING;
            break;
          case NEW:
            throw new IllegalStateException("InjectableReference is not validated yet");
          default:
            throw new IllegalStateException("Unknown state: " + state);
        }

        // if in Stage.TOOL, we only want to inject & notify toolable injection points.
        // (otherwise we'll inject all of them)
        try {
          membersInjector.injectAndNotify(
              instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
        } catch (InternalProvisionException ipe) {
          throw ipe.addSource(source);
        }
        // mark instance as ready to skip a lock on subsequent calls
        state = InjectableReferenceState.READY;
        return instance;
      } finally {
        // always release our creation lock, even on failures
        lock.unlock();
      }
    }

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


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

import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Internal respresentation of a constructor annotated with {@link AssistedInject}
 *
 * @author jmourits@google.com (Jerome Mourits)
 * @author jessewilson@google.com (Jesse Wilson)
 */
class AssistedConstructor<T> {

  private final Constructor<T> constructor;
  private final ParameterListKey assistedParameters;
  private final List<Parameter> allParameters;

  public static <T> AssistedConstructor<T> create(
      Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
    return new AssistedConstructor<T>(constructor, parameterTypes);
  }

  private AssistedConstructor(Constructor<T> constructor, List<TypeLiteral<?>> parameterTypes) {
    this.constructor = constructor;

    Annotation[][] annotations = constructor.getParameterAnnotations();

    List<Type> typeList = Lists.newArrayList();
    allParameters = new ArrayList<>();

    // categorize params as @Assisted or @Injected
    for (int i = 0; i < parameterTypes.size(); i++) {
      Parameter parameter = new Parameter(parameterTypes.get(i).getType(), annotations[i]);
      allParameters.add(parameter);
      if (parameter.isProvidedByFactory()) {
        typeList.add(parameter.getType());
      }
    }
    this.assistedParameters = new ParameterListKey(typeList);
  }

  /**
   * Returns the {@link ParameterListKey} for this constructor. The {@link ParameterListKey} is
   * created from the ordered list of {@link Assisted} constructor parameters.
   */
  public ParameterListKey getAssistedParameters() {
    return assistedParameters;
  }

  /**
   * Returns an ordered list of all constructor parameters (both {@link Assisted} and {@link
   * Inject}ed).
   */
  public List<Parameter> getAllParameters() {
    return allParameters;
  }

  public Set<Class<?>> getDeclaredExceptions() {
    return new HashSet<Class<?>>(Arrays.asList(constructor.getExceptionTypes()));
  }

  /** Returns an instance of T, constructed using this constructor, with the supplied arguments. */
  public T newInstance(Object[] args) throws Throwable {
    constructor.setAccessible(true);
    try {
      return constructor.newInstance(args);
    } catch (InvocationTargetException e) {
      throw e.getCause();
    }
  }

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


/*
 * 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.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * Central routing/dispatch class handles lifecycle of managed filters, and delegates to the servlet
 * pipeline.
 *
 * @author dhanji@gmail.com (Dhanji R. Prasanna)
 */
@Singleton
class ManagedFilterPipeline implements FilterPipeline {
  private final FilterDefinition[] filterDefinitions;
  private final ManagedServletPipeline servletPipeline;
  private final Provider<ServletContext> servletContext;

  //Unfortunately, we need the injector itself in order to create filters + servlets
  private final Injector injector;

  //Guards a DCL, so needs to be volatile
  private volatile boolean initialized = false;
  private static final TypeLiteral<FilterDefinition> FILTER_DEFS =
      TypeLiteral.get(FilterDefinition.class);

  @Inject
  public ManagedFilterPipeline(
      Injector injector,
      ManagedServletPipeline servletPipeline,
      Provider<ServletContext> servletContext) {
    this.injector = injector;
    this.servletPipeline = servletPipeline;
    this.servletContext = servletContext;

    this.filterDefinitions = collectFilterDefinitions(injector);
  }

  /**
   * Introspects the injector and collects all instances of bound {@code List<FilterDefinition>}
   * into a master list.
   *
   *
We have a guarantee that {@link com.google.inject.Injector#getBindings()} returns a map that
   * preserves insertion order in entry-set iterators.
   */
  private FilterDefinition[] collectFilterDefinitions(Injector injector) {
    List<FilterDefinition> filterDefinitions = Lists.newArrayList();
    for (Binding<FilterDefinition> entry : injector.findBindingsByType(FILTER_DEFS)) {
      filterDefinitions.add(entry.getProvider().get());
    }

    // Copy to a fixed-size array for speed of iteration.
    return filterDefinitions.toArray(new FilterDefinition[filterDefinitions.size()]);
  }

  @Override
  public synchronized void initPipeline(ServletContext servletContext) throws ServletException {

    //double-checked lock, prevents duplicate initialization
    if (initialized) return;

    // Used to prevent duplicate initialization.
    Set<Filter> initializedSoFar = Sets.newIdentityHashSet();

    for (FilterDefinition filterDefinition : filterDefinitions) {
      filterDefinition.init(servletContext, injector, initializedSoFar);
    }

    //next, initialize servlets...
    servletPipeline.init(servletContext, injector);

    //everything was ok...
    initialized = true;
  }

  @Override
  public void dispatch(
      ServletRequest request, ServletResponse response, FilterChain proceedingFilterChain)
      throws IOException, ServletException {

    //lazy init of filter pipeline (OK by the servlet specification). This is needed
    //in order for us not to force users to create a GuiceServletContextListener subclass.
    if (!initialized) {
      initPipeline(servletContext.get());
    }

    //obtain the servlet pipeline to dispatch against
    new FilterChainInvocation(filterDefinitions, servletPipeline, proceedingFilterChain)
        .doFilter(withDispatcher(request, servletPipeline), response);
  }

  /**
   * Used to create an proxy that dispatches either to the guice-servlet pipeline or the regular
   * pipeline based on uri-path match. This proxy also provides minimal forwarding support.
   *
   *
We cannot forward from a web.xml Servlet/JSP to a guice-servlet (because the filter pipeline
   * is not called again). However, we can wrap requests with our own dispatcher to forward the
   * *other* way. web.xml Servlets/JSPs can forward to themselves as per normal.
   *
   *
This is not a problem cuz we intend for people to migrate from web.xml to guice-servlet,
   * incrementally, but not the other way around (which, we should actively discourage).
   */
  @SuppressWarnings({"JavaDoc", "deprecation"})
  private ServletRequest withDispatcher(
      ServletRequest servletRequest, final ManagedServletPipeline servletPipeline) {

    // don't wrap the request if there are no servlets mapped. This prevents us from inserting our
    // wrapper unless it's actually going to be used. This is necessary for compatibility for apps
    // that downcast their HttpServletRequests to a concrete implementation.
    if (!servletPipeline.hasServletsMapped()) {
      return servletRequest;
    }

    HttpServletRequest request = (HttpServletRequest) servletRequest;
    //noinspection OverlyComplexAnonymousInnerClass
    return new HttpServletRequestWrapper(request) {

      @Override
      public RequestDispatcher getRequestDispatcher(String path) {
        final RequestDispatcher dispatcher = servletPipeline.getRequestDispatcher(path);

        return (null != dispatcher) ? dispatcher : super.getRequestDispatcher(path);
      }
    };
  }

  @Override
  public void destroyPipeline() {
    //destroy servlets first
    servletPipeline.destroy();

    //go down chain and destroy all our filters
    Set<Filter> destroyedSoFar = Sets.newIdentityHashSet();
    for (FilterDefinition filterDefinition : filterDefinitions) {
      filterDefinition.destroy(destroyedSoFar);
    }
  }
}


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

package com.google.inject.grapher;

import com.google.common.collect.SetMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Abstract injector grapher that builds the dependency graph but doesn't render it.
 *
 * @author bojand@google.com (Bojan Djordjevic)
 * @since 4.0
 */
public abstract class AbstractInjectorGrapher implements InjectorGrapher {
  private final RootKeySetCreator rootKeySetCreator;
  private final AliasCreator aliasCreator;
  private final NodeCreator nodeCreator;
  private final EdgeCreator edgeCreator;

  /**
   * Parameters used to override default settings of the grapher.
   *
   * @since 4.0
   */
  public static final class GrapherParameters {
    private RootKeySetCreator rootKeySetCreator = new DefaultRootKeySetCreator();
    private AliasCreator aliasCreator = new ProviderAliasCreator();
    private NodeCreator nodeCreator = new DefaultNodeCreator();
    private EdgeCreator edgeCreator = new DefaultEdgeCreator();

    public RootKeySetCreator getRootKeySetCreator() {
      return rootKeySetCreator;
    }

    public GrapherParameters setRootKeySetCreator(RootKeySetCreator rootKeySetCreator) {
      this.rootKeySetCreator = rootKeySetCreator;
      return this;
    }

    public AliasCreator getAliasCreator() {
      return aliasCreator;
    }

    public GrapherParameters setAliasCreator(AliasCreator aliasCreator) {
      this.aliasCreator = aliasCreator;
      return this;
    }

    public NodeCreator getNodeCreator() {
      return nodeCreator;
    }

    public GrapherParameters setNodeCreator(NodeCreator nodeCreator) {
      this.nodeCreator = nodeCreator;
      return this;
    }

    public EdgeCreator getEdgeCreator() {
      return edgeCreator;
    }

    public GrapherParameters setEdgeCreator(EdgeCreator edgeCreator) {
      this.edgeCreator = edgeCreator;
      return this;
    }
  }

  public AbstractInjectorGrapher() {
    this(new GrapherParameters());
  }

  public AbstractInjectorGrapher(GrapherParameters options) {
    this.rootKeySetCreator = options.getRootKeySetCreator();
    this.aliasCreator = options.getAliasCreator();
    this.nodeCreator = options.getNodeCreator();
    this.edgeCreator = options.getEdgeCreator();
  }

  @Override
  public final void graph(Injector injector) throws IOException {
    graph(injector, rootKeySetCreator.getRootKeys(injector));
  }

  @Override
  public final void graph(Injector injector, Set<Key<?>> root) throws IOException {
    reset();

    Iterable<Binding<?>> bindings = getBindings(injector, root);
    Map<NodeId, NodeId> aliases = resolveAliases(aliasCreator.createAliases(bindings));
    createNodes(nodeCreator.getNodes(bindings), aliases);
    createEdges(edgeCreator.getEdges(bindings), aliases);
    postProcess();
  }

  /** Resets the state of the grapher before rendering a new graph. */
  protected abstract void reset() throws IOException;

  /** Adds a new interface node to the graph. */
  protected abstract void newInterfaceNode(InterfaceNode node) throws IOException;

  /** Adds a new implementation node to the graph. */
  protected abstract void newImplementationNode(ImplementationNode node) throws IOException;

  /** Adds a new instance node to the graph. */
  protected abstract void newInstanceNode(InstanceNode node) throws IOException;

  /** Adds a new dependency edge to the graph. */
  protected abstract void newDependencyEdge(DependencyEdge edge) throws IOException;

  /** Adds a new binding edge to the graph. */
  protected abstract void newBindingEdge(BindingEdge edge) throws IOException;

  /** Performs any post processing required after all nodes and edges have been added. */
  protected abstract void postProcess() throws IOException;

  private void createNodes(Iterable<Node> nodes, Map<NodeId, NodeId> aliases) throws IOException {
    for (Node node : nodes) {
      NodeId originalId = node.getId();
      NodeId resolvedId = resolveAlias(aliases, originalId);
      node = node.copy(resolvedId);

      // Only render nodes that aren't aliased to some other node.
      if (resolvedId.equals(originalId)) {
        if (node instanceof InterfaceNode) {
          newInterfaceNode((InterfaceNode) node);
        } else if (node instanceof ImplementationNode) {
          newImplementationNode((ImplementationNode) node);
        } else {
          newInstanceNode((InstanceNode) node);
        }
      }
    }
  }

  private void createEdges(Iterable<Edge> edges, Map<NodeId, NodeId> aliases) throws IOException {
    for (Edge edge : edges) {
      edge =
          edge.copy(resolveAlias(aliases, edge.getFromId()), resolveAlias(aliases, edge.getToId()));
      if (!edge.getFromId().equals(edge.getToId())) {
        if (edge instanceof BindingEdge) {
          newBindingEdge((BindingEdge) edge);
        } else {
          newDependencyEdge((DependencyEdge) edge);
        }
      }
    }
  }

  private NodeId resolveAlias(Map<NodeId, NodeId> aliases, NodeId nodeId) {
    return aliases.getOrDefault(nodeId, nodeId);
  }

  /**
   * Transitively resolves aliases. Given aliases (X to Y) and (Y to Z), it will return mappings (X
   * to Z) and (Y to Z).
   */
  private Map<NodeId, NodeId> resolveAliases(Iterable<Alias> aliases) {
    Map<NodeId, NodeId> resolved = Maps.newHashMap();
    SetMultimap<NodeId, NodeId> inverse = HashMultimap.create();

    for (Alias alias : aliases) {
      NodeId from = alias.getFromId();
      NodeId to = alias.getToId();
      if (resolved.containsKey(to)) {
        to = resolved.get(to);
      }
      resolved.put(from, to);
      inverse.put(to, from);

      Set<NodeId> prev = inverse.get(from);
      if (prev != null) {
        for (NodeId id : prev) {
          resolved.remove(id);
          inverse.remove(from, id);
          resolved.put(id, to);
          inverse.put(to, id);
        }
      }
    }

    return resolved;
  }

  /** Returns the bindings for the root keys and their transitive dependencies. */
  private Iterable<Binding<?>> getBindings(Injector injector, Set<Key<?>> root) {
    Set<Key<?>> keys = Sets.newHashSet(root);
    Set<Key<?>> visitedKeys = Sets.newHashSet();
    List<Binding<?>> bindings = Lists.newArrayList();
    TransitiveDependencyVisitor keyVisitor = new TransitiveDependencyVisitor();

    while (!keys.isEmpty()) {
      Iterator<Key<?>> iterator = keys.iterator();
      Key<?> key = iterator.next();
      iterator.remove();

      if (!visitedKeys.contains(key)) {
        Binding<?> binding = injector.getBinding(key);
        bindings.add(binding);
        visitedKeys.add(key);
        keys.addAll(binding.acceptTargetVisitor(keyVisitor));
      }
    }
    return bindings;
  }
}