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.