Optimized InjectionPoint.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1100 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/spi/InjectionPoint.java b/src/com/google/inject/spi/InjectionPoint.java
index 312ef31..b25c915 100644
--- a/src/com/google/inject/spi/InjectionPoint.java
+++ b/src/com/google/inject/spi/InjectionPoint.java
@@ -39,11 +39,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.HashMap;
 
 /**
  * A constructor, field or method that can receive injections. Typically this is a member with the
@@ -356,6 +356,9 @@
     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());
@@ -380,135 +383,230 @@
 
   static abstract class InjectableMember {
     final TypeLiteral<?> declaringType;
-    final boolean injectable;
     final boolean optional;
     final boolean jsr330;
+    InjectableMember previous;
+    InjectableMember next;
 
-    InjectableMember(TypeLiteral<?> declaringType, AnnotatedElement member) {
+    InjectableMember(TypeLiteral<?> declaringType, Annotation atInject) {
       this.declaringType = declaringType;
 
-      javax.inject.Inject jsr330Inject = member.getAnnotation(javax.inject.Inject.class);
-      if (jsr330Inject != null) {
-        injectable = true;
+      if (atInject.annotationType() == javax.inject.Inject.class) {
         optional = false;
         jsr330 = true;
         return;
       }
 
       jsr330 = false;
-
-      Inject inject = member.getAnnotation(Inject.class);
-      if (inject != null) {
-        injectable = true;
-        optional = inject.optional();
-        return;
-      }
-
-      injectable = false;
-      optional = false;
+      optional = ((Inject) atInject).optional();
     }
 
-    abstract InjectionPoint toInjectionPoint(Errors errors);
+    abstract InjectionPoint toInjectionPoint();
   }
 
   static class InjectableField extends InjectableMember {
     final Field field;
-    InjectableField(TypeLiteral<?> declaringType, Field field) {
-      super(declaringType, field);
+    InjectableField(TypeLiteral<?> declaringType, Field field,
+        Annotation atInject) {
+      super(declaringType, atInject);
       this.field = field;
     }
 
-    InjectionPoint toInjectionPoint(Errors errors) {
+    InjectionPoint toInjectionPoint() {
       return new InjectionPoint(declaringType, field, optional);
     }
   }
 
   static class InjectableMethod extends InjectableMember {
     final Method method;
-    InjectableMethod(TypeLiteral<?> declaringType, Method method) {
-      super(declaringType, method);
+    InjectableMethod(TypeLiteral<?> declaringType, Method method,
+        Annotation atInject) {
+      super(declaringType, atInject);
       this.method = method;
     }
 
-    InjectionPoint toInjectionPoint(Errors errors) {
-      checkForMisplacedBindingAnnotations(method, errors);
+    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
+  }
+
+  static class OverrideIndex {
+    final InjectableMembers injectableMembers;
+    Map<Signature, List<InjectableMethod>> bySignature;
+    Position position = Position.TOP;
+    boolean hasMethods = false;
+
+    OverrideIndex(InjectableMembers injectableMembers) {
+      this.injectableMembers = injectableMembers;
+    }
+
+    /* Caches the signature for the last method. */
+    Method lastMethod;
+    Signature lastSignature;
+
+    void removeIfOverriddenBy(Method method) {
+      if (!hasMethods || position == Position.TOP) {
+        // There's nothing to remove.
+        return;
+      }
+
+      if (bySignature == null) {
+        // We encountered a method in a subclass. Time to index the
+        // methods in the parent class.
+        bySignature = new HashMap<Signature, List<InjectableMethod>>();
+        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<InjectableMethod>();
+          methods.add(im);
+          bySignature.put(new Signature(im.method), methods);
+        }
+      }
+
+      lastMethod = method;
+      Signature signature = lastSignature = new Signature(method);
+      List<InjectableMethod> methods = bySignature.get(signature);
+      if (methods != null) {
+        for (Iterator<InjectableMethod> iterator = methods.iterator();
+            iterator.hasNext();) {
+          InjectableMethod possiblyOverridden = iterator.next();
+          if (overrides(method, possiblyOverridden.method)) {
+            iterator.remove();
+            injectableMembers.remove(possiblyOverridden);
+          }
+        }
+      }
+    }
+
+    void add(InjectableMethod injectableMethod) {
+      hasMethods = true;
+      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
+        Signature signature = injectableMethod.method == lastMethod
+            ? lastSignature : new Signature(injectableMethod.method);
+        List<InjectableMethod> methods = bySignature.get(signature);
+        if (methods == null) {
+          methods = new ArrayList<InjectableMethod>();
+          bySignature.put(signature, methods);
+        }
+        methods.add(injectableMethod);
+      }
+    }
   }
 
   private static Set<InjectionPoint> getInjectionPoints(final TypeLiteral<?> type,
       boolean statics, Errors errors) {
-    // TODO: Turn InjectableMember into an identity wrapper. Use it as the key in
-    // injectableMembers and the values in bySignature. This will be a lot faster than
-    // hashing the Method objects.
-    LinkedHashMap<Member, InjectableMember> injectableMembers
-        = new LinkedHashMap<Member, InjectableMember>();
-    HashMap<Signature, List<Method>> bySignature = null;
+    InjectableMembers injectableMembers = new InjectableMembers();
+    OverrideIndex overrideIndex = null;
 
-    for (TypeLiteral<?> current : hierarchyFor(type)) {
+    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 : current.getRawType().getDeclaredFields()) {
         if (Modifier.isStatic(field.getModifiers()) == statics) {
-          InjectableField injectableField = new InjectableField(current, field);
-          if (injectableField.injectable) {
+          Annotation atInject = getAtInject(field);
+          if (atInject != null) {
+            InjectableField injectableField = new InjectableField(current, field, atInject);
             if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
               errors.cannotInjectFinalField(field);
-              continue;
             }
-            injectableMembers.put(field, injectableField);
+            injectableMembers.add(injectableField);
           }
         }
       }
 
       for (Method method : current.getRawType().getDeclaredMethods()) {
-        boolean isStatic = Modifier.isStatic(method.getModifiers());
-        if (isStatic == statics) {
-          InjectableMethod injectableMethod = new InjectableMethod(current, method);
-
-          if (injectableMethod.injectable) {
-            boolean result = true;
-            if (injectableMethod.jsr330) {
-              if (Modifier.isAbstract(method.getModifiers())) {
-                errors.cannotInjectAbstractMethod(method);
-                result = false;
-              }
-              if (method.getTypeParameters().length > 0) {
-                errors.cannotInjectMethodWithTypeParameters(method);
-                result = false;
-              }
-            }
-            if (!result) {
+        if (Modifier.isStatic(method.getModifiers()) == statics) {
+          if (overrideIndex != null) {
+            overrideIndex.removeIfOverriddenBy(method);
+          }
+          Annotation atInject = getAtInject(method);
+          if (atInject != null) {
+            InjectableMethod injectableMethod = new InjectableMethod(
+                current, method, atInject);
+            if (checkForMisplacedBindingAnnotations(method, errors)
+                | !isValidMethod(injectableMethod, errors)) {
               continue;
             }
-            if (isStatic && statics) {
-              injectableMembers.put(method, injectableMethod);
-            }
-          }
-
-          if (!isStatic) {
-            // Remove overridden method if present.
-            List<Method> possibleOverrides = null;
-            Signature signature = new Signature(method);
-            if (bySignature == null) {
-              // Lazily initialize the bySignature map.
-              bySignature = new HashMap<Signature, List<Method>>();
+            if (statics) {
+              injectableMembers.add(injectableMethod);
             } else {
-              possibleOverrides = bySignature.get(signature);
-              if (possibleOverrides != null) {
-                Method overridden = removeOverriddenMethod(method, possibleOverrides);
-                if (overridden != null) {
-                  injectableMembers.remove(overridden);
-                }
+              if (overrideIndex == null) {
+                overrideIndex = new OverrideIndex(injectableMembers);
               }
-            }
-
-            if (injectableMethod.injectable) {
-              // Keep track of this method in case it gets overridden.
-              if (possibleOverrides == null) {
-                possibleOverrides = new ArrayList<Method>();
-                bySignature.put(signature, possibleOverrides);
-              }
-              possibleOverrides.add(method);
-              injectableMembers.put(method, injectableMethod);
+              overrideIndex.add(injectableMethod);
             }
           }
         }
@@ -520,11 +618,12 @@
     }
 
     ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
-    for (InjectableMember injectableMember : injectableMembers.values()) {
+    for (InjectableMember im = injectableMembers.head; im != null;
+        im = im.next) {
       try {
-        builder.add(injectableMember.toInjectionPoint(errors));
+        builder.add(im.toInjectionPoint());
       } catch (ConfigurationException ignorable) {
-        if (!injectableMember.optional) {
+        if (!im.optional) {
           errors.merge(ignorable.getErrorMessages());
         }
       }
@@ -532,29 +631,33 @@
     return builder.build();
   }
 
+  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) {
-    // Construct type hierarchy from Object.class down.
     List<TypeLiteral<?>> hierarchy = new ArrayList<TypeLiteral<?>>();
     TypeLiteral<?> current = type;
     while (current.getRawType() != Object.class) {
       hierarchy.add(current);
       current = current.getSupertype(current.getRawType().getSuperclass());
     }
-    Collections.reverse(hierarchy);
     return hierarchy;
   }
 
-  private static Method removeOverriddenMethod(Method method, List<Method> possibleOverrides) {
-    for (Iterator<Method> iterator = possibleOverrides.iterator(); iterator.hasNext();) {
-      Method possibleOverride = iterator.next();
-      if (overrides(method, possibleOverride)) {
-        iterator.remove();
-        return possibleOverride;
-      }
-    }
-    return null;
-  }
-
   /**
    * 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.