blob: c9083141e12ffe0bbb763e34f5ebc5c5189cc158 [file] [log] [blame]
limpbizkit564053f2008-06-15 11:21:18 +00001/**
limpbizkita98bc7a2008-08-29 16:52:44 +00002 * Copyright (C) 2008 Google Inc.
limpbizkit564053f2008-06-15 11:21:18 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.spi;
18
limpbizkit490833f2008-11-02 00:12:39 +000019import com.google.inject.ConfigurationException;
limpbizkita98bc7a2008-08-29 16:52:44 +000020import com.google.inject.Inject;
limpbizkit564053f2008-06-15 11:21:18 +000021import com.google.inject.Key;
limpbizkit4f5d1f72008-11-14 08:43:40 +000022import com.google.inject.TypeLiteral;
limpbizkitb3a8f0b2008-09-05 22:30:40 +000023import com.google.inject.internal.Annotations;
limpbizkita98bc7a2008-08-29 16:52:44 +000024import com.google.inject.internal.Errors;
25import com.google.inject.internal.ErrorsException;
limpbizkit4272aef2008-11-23 08:10:09 +000026import static com.google.inject.internal.MoreTypes.getRawType;
limpbizkita98bc7a2008-08-29 16:52:44 +000027import com.google.inject.internal.Nullability;
sberlindfee9fd2010-10-30 14:15:06 +000028import com.google.inject.internal.util.Classes;
sberlind9c913a2011-06-26 21:02:54 +000029import com.google.common.collect.ImmutableList;
30import com.google.common.collect.ImmutableSet;
31import com.google.common.collect.Lists;
limpbizkita98bc7a2008-08-29 16:52:44 +000032import java.lang.annotation.Annotation;
limpbizkit18cb1912008-09-02 18:20:45 +000033import java.lang.reflect.AnnotatedElement;
limpbizkita98bc7a2008-08-29 16:52:44 +000034import java.lang.reflect.Constructor;
limpbizkit564053f2008-06-15 11:21:18 +000035import java.lang.reflect.Field;
36import java.lang.reflect.Member;
limpbizkita98bc7a2008-08-29 16:52:44 +000037import java.lang.reflect.Method;
limpbizkit18cb1912008-09-02 18:20:45 +000038import java.lang.reflect.Modifier;
crazybobleeeedf8162009-09-29 02:57:14 +000039import java.util.ArrayList;
limpbizkita98bc7a2008-08-29 16:52:44 +000040import java.util.Arrays;
crazybobleeeedf8162009-09-29 02:57:14 +000041import java.util.Collections;
limpbizkit@gmail.com9a227be2010-07-03 15:51:31 +000042import java.util.HashMap;
limpbizkita98bc7a2008-08-29 16:52:44 +000043import java.util.Iterator;
44import java.util.List;
crazybobleee7a727c2009-09-29 07:52:01 +000045import java.util.Map;
limpbizkit3a8c1552008-11-16 21:43:01 +000046import java.util.Set;
sberlin8ad64682010-05-12 17:10:39 +000047import java.util.logging.Level;
48import java.util.logging.Logger;
limpbizkit564053f2008-06-15 11:21:18 +000049
50/**
limpbizkitb3a8f0b2008-09-05 22:30:40 +000051 * A constructor, field or method that can receive injections. Typically this is a member with the
52 * {@literal @}{@link Inject} annotation. For non-private, no argument constructors, the member may
crazybobleeeedf8162009-09-29 02:57:14 +000053 * omit the annotation.
limpbizkit564053f2008-06-15 11:21:18 +000054 *
55 * @author crazybob@google.com (Bob Lee)
limpbizkitc489adf2008-11-18 07:01:33 +000056 * @since 2.0
limpbizkit564053f2008-06-15 11:21:18 +000057 */
limpbizkit6caa8dc2009-05-19 00:40:02 +000058public final class InjectionPoint {
sberlin8ad64682010-05-12 17:10:39 +000059
60 private static final Logger logger = Logger.getLogger(InjectionPoint.class.getName());
limpbizkit564053f2008-06-15 11:21:18 +000061
limpbizkita98bc7a2008-08-29 16:52:44 +000062 private final boolean optional;
limpbizkit564053f2008-06-15 11:21:18 +000063 private final Member member;
limpbizkit44475642009-06-20 17:30:23 +000064 private final TypeLiteral<?> declaringType;
limpbizkita98bc7a2008-08-29 16:52:44 +000065 private final ImmutableList<Dependency<?>> dependencies;
limpbizkit564053f2008-06-15 11:21:18 +000066
limpbizkit365f8342009-09-07 21:00:57 +000067 InjectionPoint(TypeLiteral<?> declaringType, Method method, boolean optional) {
limpbizkita98bc7a2008-08-29 16:52:44 +000068 this.member = method;
limpbizkit44475642009-06-20 17:30:23 +000069 this.declaringType = declaringType;
limpbizkit365f8342009-09-07 21:00:57 +000070 this.optional = optional;
limpbizkit44475642009-06-20 17:30:23 +000071 this.dependencies = forMember(method, declaringType, method.getParameterAnnotations());
limpbizkita98bc7a2008-08-29 16:52:44 +000072 }
73
limpbizkit44475642009-06-20 17:30:23 +000074 InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
limpbizkita98bc7a2008-08-29 16:52:44 +000075 this.member = constructor;
limpbizkit44475642009-06-20 17:30:23 +000076 this.declaringType = declaringType;
limpbizkita98bc7a2008-08-29 16:52:44 +000077 this.optional = false;
limpbizkit44475642009-06-20 17:30:23 +000078 this.dependencies = forMember(
79 constructor, declaringType, constructor.getParameterAnnotations());
limpbizkita98bc7a2008-08-29 16:52:44 +000080 }
81
limpbizkit365f8342009-09-07 21:00:57 +000082 InjectionPoint(TypeLiteral<?> declaringType, Field field, boolean optional) {
limpbizkita98bc7a2008-08-29 16:52:44 +000083 this.member = field;
limpbizkit44475642009-06-20 17:30:23 +000084 this.declaringType = declaringType;
limpbizkit365f8342009-09-07 21:00:57 +000085 this.optional = optional;
limpbizkita98bc7a2008-08-29 16:52:44 +000086
87 Annotation[] annotations = field.getAnnotations();
limpbizkit18cb1912008-09-02 18:20:45 +000088
89 Errors errors = new Errors(field);
90 Key<?> key = null;
91 try {
limpbizkit44475642009-06-20 17:30:23 +000092 key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
limpbizkit39e98862009-06-21 18:13:01 +000093 } catch (ConfigurationException e) {
94 errors.merge(e.getErrorMessages());
limpbizkit18cb1912008-09-02 18:20:45 +000095 } catch (ErrorsException e) {
96 errors.merge(e.getErrors());
97 }
limpbizkit06898062008-11-02 05:14:55 +000098 errors.throwConfigurationExceptionIfErrorsExist();
limpbizkit18cb1912008-09-02 18:20:45 +000099
limpbizkita98bc7a2008-08-29 16:52:44 +0000100 this.dependencies = ImmutableList.<Dependency<?>>of(
101 newDependency(key, Nullability.allowsNull(annotations), -1));
102 }
103
limpbizkit4f5d1f72008-11-14 08:43:40 +0000104 private ImmutableList<Dependency<?>> forMember(Member member, TypeLiteral<?> type,
limpbizkit8ba97882008-11-04 02:52:54 +0000105 Annotation[][] paramterAnnotations) {
limpbizkit18cb1912008-09-02 18:20:45 +0000106 Errors errors = new Errors(member);
limpbizkit8ba97882008-11-04 02:52:54 +0000107 Iterator<Annotation[]> annotationsIterator = Arrays.asList(paramterAnnotations).iterator();
limpbizkita98bc7a2008-08-29 16:52:44 +0000108
109 List<Dependency<?>> dependencies = Lists.newArrayList();
110 int index = 0;
limpbizkit8ba97882008-11-04 02:52:54 +0000111
limpbizkit4272aef2008-11-23 08:10:09 +0000112 for (TypeLiteral<?> parameterType : type.getParameterTypes(member)) {
limpbizkita98bc7a2008-08-29 16:52:44 +0000113 try {
114 Annotation[] parameterAnnotations = annotationsIterator.next();
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000115 Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
limpbizkita98bc7a2008-08-29 16:52:44 +0000116 dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
117 index++;
limpbizkit39e98862009-06-21 18:13:01 +0000118 } catch (ConfigurationException e) {
119 errors.merge(e.getErrorMessages());
limpbizkita98bc7a2008-08-29 16:52:44 +0000120 } catch (ErrorsException e) {
121 errors.merge(e.getErrors());
122 }
123 }
124
limpbizkit06898062008-11-02 05:14:55 +0000125 errors.throwConfigurationExceptionIfErrorsExist();
limpbizkita98bc7a2008-08-29 16:52:44 +0000126 return ImmutableList.copyOf(dependencies);
127 }
128
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000129 // This metohd is necessary to create a Dependency<T> with proper generic type information
limpbizkita98bc7a2008-08-29 16:52:44 +0000130 private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
131 return new Dependency<T>(this, key, allowsNull, parameterIndex);
limpbizkit564053f2008-06-15 11:21:18 +0000132 }
133
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000134 /**
135 * Returns the injected constructor, field, or method.
136 */
limpbizkit564053f2008-06-15 11:21:18 +0000137 public Member getMember() {
crazybobleeeedf8162009-09-29 02:57:14 +0000138 // TODO: Don't expose the original member (which probably has setAccessible(true)).
limpbizkit564053f2008-06-15 11:21:18 +0000139 return member;
140 }
141
limpbizkita98bc7a2008-08-29 16:52:44 +0000142 /**
143 * Returns the dependencies for this injection point. If the injection point is for a method or
144 * constructor, the dependencies will correspond to that member's parameters. Field injection
145 * points always have a single dependency for the field itself.
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000146 *
147 * @return a possibly-empty list
limpbizkita98bc7a2008-08-29 16:52:44 +0000148 */
149 public List<Dependency<?>> getDependencies() {
150 return dependencies;
limpbizkit564053f2008-06-15 11:21:18 +0000151 }
152
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000153 /**
154 * Returns true if this injection point shall be skipped if the injector cannot resolve bindings
155 * for all required dependencies. Both explicit bindings (as specified in a module), and implicit
156 * bindings ({@literal @}{@link com.google.inject.ImplementedBy ImplementedBy}, default
157 * constructors etc.) may be used to satisfy optional injection points.
158 */
limpbizkita98bc7a2008-08-29 16:52:44 +0000159 public boolean isOptional() {
160 return optional;
161 }
sberlin97c22712010-02-05 21:12:05 +0000162
163 /**
164 * Returns true if the element is annotated with {@literal @}{@link Toolable}.
sberline1290f32010-07-03 15:42:51 +0000165 *
166 * @since 3.0
sberlin97c22712010-02-05 21:12:05 +0000167 */
168 public boolean isToolable() {
169 return ((AnnotatedElement)member).isAnnotationPresent(Toolable.class);
170 }
limpbizkita98bc7a2008-08-29 16:52:44 +0000171
limpbizkit44475642009-06-20 17:30:23 +0000172 /**
173 * Returns the generic type that defines this injection point. If the member exists on a
174 * parameterized type, the result will include more type information than the member's {@link
175 * Member#getDeclaringClass() raw declaring class}.
sberline1290f32010-07-03 15:42:51 +0000176 *
177 * @since 3.0
limpbizkit44475642009-06-20 17:30:23 +0000178 */
179 public TypeLiteral<?> getDeclaringType() {
180 return declaringType;
181 }
182
limpbizkita98bc7a2008-08-29 16:52:44 +0000183 @Override public boolean equals(Object o) {
184 return o instanceof InjectionPoint
limpbizkit62600d22009-06-20 18:01:04 +0000185 && member.equals(((InjectionPoint) o).member)
186 && declaringType.equals(((InjectionPoint) o).declaringType);
limpbizkita98bc7a2008-08-29 16:52:44 +0000187 }
188
189 @Override public int hashCode() {
limpbizkit62600d22009-06-20 18:01:04 +0000190 return member.hashCode() ^ declaringType.hashCode();
limpbizkit564053f2008-06-15 11:21:18 +0000191 }
192
limpbizkit76a2b9a2008-08-14 04:58:23 +0000193 @Override public String toString() {
sberlindfee9fd2010-10-30 14:15:06 +0000194 return Classes.toString(member);
limpbizkit564053f2008-06-15 11:21:18 +0000195 }
196
limpbizkita98bc7a2008-08-29 16:52:44 +0000197 /**
limpbizkit44475642009-06-20 17:30:23 +0000198 * Returns a new injection point for the specified constructor. If the declaring type of {@code
199 * constructor} is parameterized (such as {@code List<T>}), prefer the overload that includes a
200 * type literal.
limpbizkit9b8bdc72009-06-07 19:37:28 +0000201 *
202 * @param constructor any single constructor present on {@code type}.
sberline1290f32010-07-03 15:42:51 +0000203 *
204 * @since 3.0
limpbizkit9b8bdc72009-06-07 19:37:28 +0000205 */
206 public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
limpbizkit9b8bdc72009-06-07 19:37:28 +0000207 return new InjectionPoint(TypeLiteral.get(constructor.getDeclaringClass()), constructor);
208 }
209
210 /**
211 * Returns a new injection point for the specified constructor of {@code type}.
212 *
213 * @param constructor any single constructor present on {@code type}.
214 * @param type the concrete type that defines {@code constructor}.
sberline1290f32010-07-03 15:42:51 +0000215 *
216 * @since 3.0
limpbizkit9b8bdc72009-06-07 19:37:28 +0000217 */
218 public static <T> InjectionPoint forConstructor(
219 Constructor<T> constructor, TypeLiteral<? extends T> type) {
220 if (type.getRawType() != constructor.getDeclaringClass()) {
221 new Errors(type)
222 .constructorNotDefinedByType(constructor, type)
223 .throwConfigurationExceptionIfErrorsExist();
224 }
225
limpbizkit9b8bdc72009-06-07 19:37:28 +0000226 return new InjectionPoint(type, constructor);
227 }
228
229 /**
limpbizkita6e0e782008-09-03 06:19:56 +0000230 * Returns a new injection point for the injectable constructor of {@code type}.
231 *
232 * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
233 * or a no-arguments constructor that is not private.
limpbizkit8ba97882008-11-04 02:52:54 +0000234 * @throws ConfigurationException if there is no injectable constructor, more than one injectable
limpbizkita6e0e782008-09-03 06:19:56 +0000235 * constructor, or if parameters of the injectable constructor are malformed, such as a
236 * parameter with multiple binding annotations.
237 */
limpbizkit3a8c1552008-11-16 21:43:01 +0000238 public static InjectionPoint forConstructorOf(TypeLiteral<?> type) {
limpbizkit4272aef2008-11-23 08:10:09 +0000239 Class<?> rawType = getRawType(type.getType());
limpbizkit3a8c1552008-11-16 21:43:01 +0000240 Errors errors = new Errors(rawType);
limpbizkita6e0e782008-09-03 06:19:56 +0000241
limpbizkit06898062008-11-02 05:14:55 +0000242 Constructor<?> injectableConstructor = null;
limpbizkit8ba97882008-11-04 02:52:54 +0000243 for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {
limpbizkita6e0e782008-09-03 06:19:56 +0000244
limpbizkit365f8342009-09-07 21:00:57 +0000245 boolean optional;
246 Inject guiceInject = constructor.getAnnotation(Inject.class);
247 if (guiceInject == null) {
248 javax.inject.Inject javaxInject = constructor.getAnnotation(javax.inject.Inject.class);
249 if (javaxInject == null) {
250 continue;
limpbizkita6e0e782008-09-03 06:19:56 +0000251 }
limpbizkit2acabce2009-09-28 02:51:24 +0000252 optional = false;
limpbizkit365f8342009-09-07 21:00:57 +0000253 } else {
254 optional = guiceInject.optional();
limpbizkita6e0e782008-09-03 06:19:56 +0000255 }
limpbizkit365f8342009-09-07 21:00:57 +0000256
257 if (optional) {
258 errors.optionalConstructor(constructor);
259 }
260
261 if (injectableConstructor != null) {
262 errors.tooManyConstructors(rawType);
263 }
264
265 injectableConstructor = constructor;
266 checkForMisplacedBindingAnnotations(injectableConstructor, errors);
limpbizkita6e0e782008-09-03 06:19:56 +0000267 }
268
limpbizkit06898062008-11-02 05:14:55 +0000269 errors.throwConfigurationExceptionIfErrorsExist();
limpbizkita6e0e782008-09-03 06:19:56 +0000270
limpbizkit06898062008-11-02 05:14:55 +0000271 if (injectableConstructor != null) {
limpbizkit3a8c1552008-11-16 21:43:01 +0000272 return new InjectionPoint(type, injectableConstructor);
limpbizkita6e0e782008-09-03 06:19:56 +0000273 }
274
limpbizkit06898062008-11-02 05:14:55 +0000275 // If no annotated constructor is found, look for a no-arg constructor instead.
limpbizkita6e0e782008-09-03 06:19:56 +0000276 try {
limpbizkit8ba97882008-11-04 02:52:54 +0000277 Constructor<?> noArgConstructor = rawType.getDeclaredConstructor();
limpbizkita6e0e782008-09-03 06:19:56 +0000278
279 // Disallow private constructors on non-private classes (unless they have @Inject)
limpbizkit06898062008-11-02 05:14:55 +0000280 if (Modifier.isPrivate(noArgConstructor.getModifiers())
limpbizkit8ba97882008-11-04 02:52:54 +0000281 && !Modifier.isPrivate(rawType.getModifiers())) {
282 errors.missingConstructor(rawType);
limpbizkit490833f2008-11-02 00:12:39 +0000283 throw new ConfigurationException(errors.getMessages());
limpbizkita6e0e782008-09-03 06:19:56 +0000284 }
285
limpbizkit06898062008-11-02 05:14:55 +0000286 checkForMisplacedBindingAnnotations(noArgConstructor, errors);
limpbizkit3a8c1552008-11-16 21:43:01 +0000287 return new InjectionPoint(type, noArgConstructor);
limpbizkita6e0e782008-09-03 06:19:56 +0000288 } catch (NoSuchMethodException e) {
limpbizkit8ba97882008-11-04 02:52:54 +0000289 errors.missingConstructor(rawType);
limpbizkit490833f2008-11-02 00:12:39 +0000290 throw new ConfigurationException(errors.getMessages());
limpbizkita6e0e782008-09-03 06:19:56 +0000291 }
292 }
293
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000294 /**
limpbizkit3a8c1552008-11-16 21:43:01 +0000295 * Returns a new injection point for the injectable constructor of {@code type}.
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000296 *
limpbizkit3a8c1552008-11-16 21:43:01 +0000297 * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
298 * or a no-arguments constructor that is not private.
299 * @throws ConfigurationException if there is no injectable constructor, more than one injectable
300 * constructor, or if parameters of the injectable constructor are malformed, such as a
301 * parameter with multiple binding annotations.
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000302 */
limpbizkit3a8c1552008-11-16 21:43:01 +0000303 public static InjectionPoint forConstructorOf(Class<?> type) {
304 return forConstructorOf(TypeLiteral.get(type));
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000305 }
306
307 /**
limpbizkitc45600e2008-12-27 02:57:04 +0000308 * Returns all static method and field injection points on {@code type}.
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000309 *
limpbizkitc45600e2008-12-27 02:57:04 +0000310 * @return a possibly empty set of injection points. The set has a specified iteration order. All
311 * fields are returned and then all methods. Within the fields, supertype fields are returned
312 * before subtype fields. Similarly, supertype methods are returned before subtype methods.
limpbizkit8ba97882008-11-04 02:52:54 +0000313 * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
limpbizkit3a8c1552008-11-16 21:43:01 +0000314 * a field with multiple binding annotations. The exception's {@link
315 * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
316 * of the valid injection points.
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000317 */
crazyboblee3c84c572009-09-29 04:32:55 +0000318 public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral<?> type) {
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000319 Errors errors = new Errors();
limpbizkit3a8c1552008-11-16 21:43:01 +0000320
crazybobleeeedf8162009-09-29 02:57:14 +0000321 Set<InjectionPoint> result = getInjectionPoints(type, true, errors);
limpbizkit3a8c1552008-11-16 21:43:01 +0000322 if (errors.hasErrors()) {
323 throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
324 }
325 return result;
326 }
limpbizkitc45600e2008-12-27 02:57:04 +0000327
limpbizkit3a8c1552008-11-16 21:43:01 +0000328 /**
limpbizkitc45600e2008-12-27 02:57:04 +0000329 * Returns all static method and field injection points on {@code type}.
limpbizkit3a8c1552008-11-16 21:43:01 +0000330 *
limpbizkitc45600e2008-12-27 02:57:04 +0000331 * @return a possibly empty set of injection points. The set has a specified iteration order. All
332 * fields are returned and then all methods. Within the fields, supertype fields are returned
333 * before subtype fields. Similarly, supertype methods are returned before subtype methods.
limpbizkit3a8c1552008-11-16 21:43:01 +0000334 * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
335 * a field with multiple binding annotations. The exception's {@link
336 * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
337 * of the valid injection points.
338 */
339 public static Set<InjectionPoint> forStaticMethodsAndFields(Class<?> type) {
340 return forStaticMethodsAndFields(TypeLiteral.get(type));
341 }
342
343 /**
limpbizkitc45600e2008-12-27 02:57:04 +0000344 * Returns all instance method and field injection points on {@code type}.
limpbizkit3a8c1552008-11-16 21:43:01 +0000345 *
limpbizkitc45600e2008-12-27 02:57:04 +0000346 * @return a possibly empty set of injection points. The set has a specified iteration order. All
347 * fields are returned and then all methods. Within the fields, supertype fields are returned
348 * before subtype fields. Similarly, supertype methods are returned before subtype methods.
limpbizkit3a8c1552008-11-16 21:43:01 +0000349 * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
350 * a field with multiple binding annotations. The exception's {@link
351 * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
352 * of the valid injection points.
353 */
354 public static Set<InjectionPoint> forInstanceMethodsAndFields(TypeLiteral<?> type) {
limpbizkit3a8c1552008-11-16 21:43:01 +0000355 Errors errors = new Errors();
crazybobleeeedf8162009-09-29 02:57:14 +0000356 Set<InjectionPoint> result = getInjectionPoints(type, false, errors);
limpbizkit3a8c1552008-11-16 21:43:01 +0000357 if (errors.hasErrors()) {
358 throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
359 }
360 return result;
361 }
362
363 /**
limpbizkitc45600e2008-12-27 02:57:04 +0000364 * Returns all instance method and field injection points on {@code type}.
limpbizkit3a8c1552008-11-16 21:43:01 +0000365 *
limpbizkitc45600e2008-12-27 02:57:04 +0000366 * @return a possibly empty set of injection points. The set has a specified iteration order. All
367 * fields are returned and then all methods. Within the fields, supertype fields are returned
368 * before subtype fields. Similarly, supertype methods are returned before subtype methods.
limpbizkit3a8c1552008-11-16 21:43:01 +0000369 * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
370 * a field with multiple binding annotations. The exception's {@link
371 * ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
372 * of the valid injection points.
373 */
374 public static Set<InjectionPoint> forInstanceMethodsAndFields(Class<?> type) {
375 return forInstanceMethodsAndFields(TypeLiteral.get(type));
limpbizkit06898062008-11-02 05:14:55 +0000376 }
377
crazybobleee7a727c2009-09-29 07:52:01 +0000378 /**
379 * Returns true if the binding annotation is in the wrong place.
380 */
crazybobleeeedf8162009-09-29 02:57:14 +0000381 private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) {
limpbizkit06898062008-11-02 05:14:55 +0000382 Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation(
383 errors, member, ((AnnotatedElement) member).getAnnotations());
limpbizkit9bf65852008-11-29 22:01:13 +0000384 if (misplacedBindingAnnotation == null) {
crazybobleeeedf8162009-09-29 02:57:14 +0000385 return false;
limpbizkit06898062008-11-02 05:14:55 +0000386 }
limpbizkit9bf65852008-11-29 22:01:13 +0000387
388 // don't warn about misplaced binding annotations on methods when there's a field with the same
389 // name. In Scala, fields always get accessor methods (that we need to ignore). See bug 242.
390 if (member instanceof Method) {
391 try {
392 if (member.getDeclaringClass().getDeclaredField(member.getName()) != null) {
crazybobleeeedf8162009-09-29 02:57:14 +0000393 return false;
limpbizkit9bf65852008-11-29 22:01:13 +0000394 }
395 } catch (NoSuchFieldException ignore) {
396 }
397 }
398
399 errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation);
crazybobleeeedf8162009-09-29 02:57:14 +0000400 return true;
limpbizkitb3a8f0b2008-09-05 22:30:40 +0000401 }
402
crazyboblee6383ccd2009-09-29 18:11:40 +0000403 /**
404 * Node in the doubly-linked list of injectable members (fields and methods).
405 */
crazybobleeeedf8162009-09-29 02:57:14 +0000406 static abstract class InjectableMember {
407 final TypeLiteral<?> declaringType;
crazybobleeeedf8162009-09-29 02:57:14 +0000408 final boolean optional;
409 final boolean jsr330;
crazybobleee7a727c2009-09-29 07:52:01 +0000410 InjectableMember previous;
411 InjectableMember next;
crazybobleeeedf8162009-09-29 02:57:14 +0000412
crazybobleee7a727c2009-09-29 07:52:01 +0000413 InjectableMember(TypeLiteral<?> declaringType, Annotation atInject) {
crazybobleeeedf8162009-09-29 02:57:14 +0000414 this.declaringType = declaringType;
415
crazybobleee7a727c2009-09-29 07:52:01 +0000416 if (atInject.annotationType() == javax.inject.Inject.class) {
crazybobleeeedf8162009-09-29 02:57:14 +0000417 optional = false;
418 jsr330 = true;
419 return;
420 }
421
422 jsr330 = false;
crazybobleee7a727c2009-09-29 07:52:01 +0000423 optional = ((Inject) atInject).optional();
limpbizkit18cb1912008-09-02 18:20:45 +0000424 }
425
crazybobleee7a727c2009-09-29 07:52:01 +0000426 abstract InjectionPoint toInjectionPoint();
limpbizkit18cb1912008-09-02 18:20:45 +0000427 }
428
crazybobleeeedf8162009-09-29 02:57:14 +0000429 static class InjectableField extends InjectableMember {
430 final Field field;
crazybobleee7a727c2009-09-29 07:52:01 +0000431 InjectableField(TypeLiteral<?> declaringType, Field field,
432 Annotation atInject) {
433 super(declaringType, atInject);
crazybobleeeedf8162009-09-29 02:57:14 +0000434 this.field = field;
435 }
436
crazybobleee7a727c2009-09-29 07:52:01 +0000437 InjectionPoint toInjectionPoint() {
crazybobleeeedf8162009-09-29 02:57:14 +0000438 return new InjectionPoint(declaringType, field, optional);
439 }
440 }
441
442 static class InjectableMethod extends InjectableMember {
443 final Method method;
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000444 /**
445 * true if this method overrode a method that was annotated
446 * with com.google.inject.Inject. used to allow different
447 * override behavior for guice inject vs javax.inject.Inject
448 */
449 boolean overrodeGuiceInject;
crazybobleee7a727c2009-09-29 07:52:01 +0000450 InjectableMethod(TypeLiteral<?> declaringType, Method method,
451 Annotation atInject) {
452 super(declaringType, atInject);
crazybobleeeedf8162009-09-29 02:57:14 +0000453 this.method = method;
454 }
455
crazybobleee7a727c2009-09-29 07:52:01 +0000456 InjectionPoint toInjectionPoint() {
crazybobleeeedf8162009-09-29 02:57:14 +0000457 return new InjectionPoint(declaringType, method, optional);
458 }
crazybobleee7a727c2009-09-29 07:52:01 +0000459
460 public boolean isFinal() {
461 return Modifier.isFinal(method.getModifiers());
462 }
463 }
464
465 static Annotation getAtInject(AnnotatedElement member) {
466 Annotation a = member.getAnnotation(javax.inject.Inject.class);
467 return a == null ? member.getAnnotation(Inject.class) : a;
468 }
469
470 /**
471 * Linked list of injectable members.
472 */
473 static class InjectableMembers {
474 InjectableMember head;
475 InjectableMember tail;
476
477 void add(InjectableMember member) {
478 if (head == null) {
479 head = tail = member;
480 } else {
481 member.previous = tail;
482 tail.next = member;
483 tail = member;
484 }
485 }
486
487 void remove(InjectableMember member) {
488 if (member.previous != null) {
489 member.previous.next = member.next;
490 }
491 if (member.next != null) {
492 member.next.previous = member.previous;
493 }
494 if (head == member) {
495 head = member.next;
496 }
497 if (tail == member) {
498 tail = member.previous;
499 }
500 }
501
502 boolean isEmpty() {
503 return head == null;
504 }
505 }
506
507 /** Position in type hierarchy. */
508 enum Position {
509 TOP, // No need to check for overridden methods
510 MIDDLE,
511 BOTTOM // Methods won't be overridden
512 }
513
crazyboblee8fbdc4e2009-09-29 18:05:06 +0000514 /**
515 * Keeps track of injectable methods so we can remove methods that get overridden in O(1) time.
516 * Uses our position in the type hierarchy to perform optimizations.
517 */
crazybobleee7a727c2009-09-29 07:52:01 +0000518 static class OverrideIndex {
519 final InjectableMembers injectableMembers;
520 Map<Signature, List<InjectableMethod>> bySignature;
521 Position position = Position.TOP;
crazybobleee7a727c2009-09-29 07:52:01 +0000522
523 OverrideIndex(InjectableMembers injectableMembers) {
524 this.injectableMembers = injectableMembers;
525 }
526
527 /* Caches the signature for the last method. */
528 Method lastMethod;
529 Signature lastSignature;
530
crazybobleeed61f2c2009-09-29 18:13:40 +0000531 /**
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000532 * Removes a method overridden by the given method, if present. In order to
533 * remain backwards compatible with prior Guice versions, this will *not*
534 * remove overridden methods if 'alwaysRemove' is false and the overridden
535 * signature was annotated with a com.google.inject.Inject.
536 *
537 * @param method
538 * The method used to determine what is overridden and should be
539 * removed.
540 * @param alwaysRemove
541 * true if overridden methods should be removed even if they were
542 * guice @Inject
543 * @param injectableMethod
544 * if this method overrode any guice @Inject methods,
545 * {@link InjectableMethod#overrodeGuiceInject} is set to true
crazybobleeed61f2c2009-09-29 18:13:40 +0000546 */
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000547 boolean removeIfOverriddenBy(Method method, boolean alwaysRemove,
548 InjectableMethod injectableMethod) {
crazyboblee6ee36342009-09-29 18:07:22 +0000549 if (position == Position.TOP) {
550 // If we're at the top of the hierarchy, there's nothing to override.
sberlin8ad64682010-05-12 17:10:39 +0000551 return false;
crazybobleee7a727c2009-09-29 07:52:01 +0000552 }
553
554 if (bySignature == null) {
555 // We encountered a method in a subclass. Time to index the
556 // methods in the parent class.
557 bySignature = new HashMap<Signature, List<InjectableMethod>>();
558 for (InjectableMember member = injectableMembers.head; member != null;
559 member = member.next) {
560 if (!(member instanceof InjectableMethod)) continue;
561 InjectableMethod im = (InjectableMethod) member;
562 if (im.isFinal()) continue;
563 List<InjectableMethod> methods = new ArrayList<InjectableMethod>();
564 methods.add(im);
565 bySignature.put(new Signature(im.method), methods);
566 }
567 }
568
569 lastMethod = method;
570 Signature signature = lastSignature = new Signature(method);
571 List<InjectableMethod> methods = bySignature.get(signature);
sberlin8ad64682010-05-12 17:10:39 +0000572 boolean removed = false;
crazybobleee7a727c2009-09-29 07:52:01 +0000573 if (methods != null) {
574 for (Iterator<InjectableMethod> iterator = methods.iterator();
575 iterator.hasNext();) {
576 InjectableMethod possiblyOverridden = iterator.next();
577 if (overrides(method, possiblyOverridden.method)) {
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000578 boolean wasGuiceInject =
579 !possiblyOverridden.jsr330 || possiblyOverridden.overrodeGuiceInject;
580 if(injectableMethod != null) {
581 injectableMethod.overrodeGuiceInject = wasGuiceInject;
582 }
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000583 // Only actually remove the methods if we want to force
584 // remove or if the signature never specified @com.google.inject.Inject
585 // somewhere.
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000586 if(alwaysRemove || !wasGuiceInject) {
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000587 removed = true;
588 iterator.remove();
589 injectableMembers.remove(possiblyOverridden);
590 }
crazybobleee7a727c2009-09-29 07:52:01 +0000591 }
592 }
593 }
sberlin8ad64682010-05-12 17:10:39 +0000594 return removed;
crazybobleee7a727c2009-09-29 07:52:01 +0000595 }
596
crazybobleeed61f2c2009-09-29 18:13:40 +0000597 /**
598 * Adds the given method to the list of injection points. Keeps track of it in this index
599 * in case it gets overridden.
600 */
crazybobleee7a727c2009-09-29 07:52:01 +0000601 void add(InjectableMethod injectableMethod) {
crazybobleee7a727c2009-09-29 07:52:01 +0000602 injectableMembers.add(injectableMethod);
603 if (position == Position.BOTTOM
604 || injectableMethod.isFinal()) {
605 // This method can't be overridden, so there's no need to index it.
606 return;
607 }
608 if (bySignature != null) {
609 // Try to reuse the signature we created during removal
610 Signature signature = injectableMethod.method == lastMethod
611 ? lastSignature : new Signature(injectableMethod.method);
612 List<InjectableMethod> methods = bySignature.get(signature);
613 if (methods == null) {
614 methods = new ArrayList<InjectableMethod>();
615 bySignature.put(signature, methods);
616 }
617 methods.add(injectableMethod);
618 }
619 }
crazybobleeeedf8162009-09-29 02:57:14 +0000620 }
621
crazyboblee6383ccd2009-09-29 18:11:40 +0000622 /**
623 * Returns an ordered, immutable set of injection points for the given type. Members in
624 * superclasses come before members in subclasses. Within a class, fields come before methods.
625 * Overridden methods are filtered out.
626 *
627 * @param statics true is this method should return static members, false for instance members
628 * @param errors used to record errors
629 */
crazybobleeeedf8162009-09-29 02:57:14 +0000630 private static Set<InjectionPoint> getInjectionPoints(final TypeLiteral<?> type,
631 boolean statics, Errors errors) {
crazybobleee7a727c2009-09-29 07:52:01 +0000632 InjectableMembers injectableMembers = new InjectableMembers();
633 OverrideIndex overrideIndex = null;
crazybobleeeedf8162009-09-29 02:57:14 +0000634
crazybobleee7a727c2009-09-29 07:52:01 +0000635 List<TypeLiteral<?>> hierarchy = hierarchyFor(type);
636 int topIndex = hierarchy.size() - 1;
637 for (int i = topIndex; i >= 0; i--) {
638 if (overrideIndex != null && i < topIndex) {
639 // Knowing the position within the hierarchy helps us make optimizations.
640 if (i == 0) {
641 overrideIndex.position = Position.BOTTOM;
642 } else {
643 overrideIndex.position = Position.MIDDLE;
644 }
645 }
646
647 TypeLiteral<?> current = hierarchy.get(i);
648
crazybobleeeedf8162009-09-29 02:57:14 +0000649 for (Field field : current.getRawType().getDeclaredFields()) {
650 if (Modifier.isStatic(field.getModifiers()) == statics) {
crazybobleee7a727c2009-09-29 07:52:01 +0000651 Annotation atInject = getAtInject(field);
652 if (atInject != null) {
653 InjectableField injectableField = new InjectableField(current, field, atInject);
crazybobleeeedf8162009-09-29 02:57:14 +0000654 if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
655 errors.cannotInjectFinalField(field);
crazybobleeeedf8162009-09-29 02:57:14 +0000656 }
crazybobleee7a727c2009-09-29 07:52:01 +0000657 injectableMembers.add(injectableField);
crazybobleeeedf8162009-09-29 02:57:14 +0000658 }
659 }
limpbizkit18cb1912008-09-02 18:20:45 +0000660 }
661
crazybobleeeedf8162009-09-29 02:57:14 +0000662 for (Method method : current.getRawType().getDeclaredMethods()) {
crazybobleee7a727c2009-09-29 07:52:01 +0000663 if (Modifier.isStatic(method.getModifiers()) == statics) {
crazybobleee7a727c2009-09-29 07:52:01 +0000664 Annotation atInject = getAtInject(method);
665 if (atInject != null) {
666 InjectableMethod injectableMethod = new InjectableMethod(
667 current, method, atInject);
668 if (checkForMisplacedBindingAnnotations(method, errors)
669 | !isValidMethod(injectableMethod, errors)) {
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000670 if (overrideIndex != null) {
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000671 boolean removed = overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000672 if(removed) {
673 logger.log(Level.WARNING, "Method: {0} is not a valid injectable method ("
674 + "because it either has misplaced binding annotations "
675 + "or specifies type parameters) but is overriding a method that is valid. "
676 + "Because it is not valid, the method will not be injected. "
677 + "To fix this, make the method a valid injectable method.", method);
678 }
sberlin8ad64682010-05-12 17:10:39 +0000679 }
crazybobleeeedf8162009-09-29 02:57:14 +0000680 continue;
681 }
crazybobleee7a727c2009-09-29 07:52:01 +0000682 if (statics) {
683 injectableMembers.add(injectableMethod);
crazybobleeeedf8162009-09-29 02:57:14 +0000684 } else {
crazybobleee7a727c2009-09-29 07:52:01 +0000685 if (overrideIndex == null) {
crazyboblee570c1812009-09-29 18:00:49 +0000686 /*
687 * Creating the override index lazily means that the first type in the hierarchy
crazyboblee8fbdc4e2009-09-29 18:05:06 +0000688 * with injectable methods (not necessarily the top most type) will be treated as
crazyboblee570c1812009-09-29 18:00:49 +0000689 * the TOP position and will enjoy the same optimizations (no checks for overridden
690 * methods, etc.).
691 */
crazybobleee7a727c2009-09-29 07:52:01 +0000692 overrideIndex = new OverrideIndex(injectableMembers);
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000693 } else {
694 // Forcibly remove the overriden method, otherwise we'll inject
695 // it twice.
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000696 overrideIndex.removeIfOverriddenBy(method, true, injectableMethod);
crazybobleeeedf8162009-09-29 02:57:14 +0000697 }
crazybobleee7a727c2009-09-29 07:52:01 +0000698 overrideIndex.add(injectableMethod);
crazybobleeeedf8162009-09-29 02:57:14 +0000699 }
sberlin8ad64682010-05-12 17:10:39 +0000700 } else {
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000701 if(overrideIndex != null) {
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000702 boolean removed = overrideIndex.removeIfOverriddenBy(method, false, null);
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000703 if(removed) {
704 logger.log(Level.WARNING, "Method: {0} is not annotated with @Inject but "
sberlin@gmail.come2a8afd2010-06-23 05:13:56 +0000705 + "is overriding a method that is annotated with @javax.inject.Inject. Because "
sberlin@gmail.comf51c6902010-06-22 17:20:57 +0000706 + "it is not annotated with @Inject, the method will not be injected. "
707 + "To fix this, annotate the method with @Inject.", method);
708 }
sberlin8ad64682010-05-12 17:10:39 +0000709 }
crazybobleeeedf8162009-09-29 02:57:14 +0000710 }
711 }
712 }
713 }
714
715 if (injectableMembers.isEmpty()) {
716 return Collections.emptySet();
717 }
718
719 ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
crazybobleee7a727c2009-09-29 07:52:01 +0000720 for (InjectableMember im = injectableMembers.head; im != null;
721 im = im.next) {
limpbizkit18cb1912008-09-02 18:20:45 +0000722 try {
crazybobleee7a727c2009-09-29 07:52:01 +0000723 builder.add(im.toInjectionPoint());
limpbizkit490833f2008-11-02 00:12:39 +0000724 } catch (ConfigurationException ignorable) {
crazybobleee7a727c2009-09-29 07:52:01 +0000725 if (!im.optional) {
limpbizkit490833f2008-11-02 00:12:39 +0000726 errors.merge(ignorable.getErrorMessages());
limpbizkit18cb1912008-09-02 18:20:45 +0000727 }
728 }
729 }
crazybobleeeedf8162009-09-29 02:57:14 +0000730 return builder.build();
limpbizkit18cb1912008-09-02 18:20:45 +0000731 }
732
crazybobleee7a727c2009-09-29 07:52:01 +0000733 private static boolean isValidMethod(InjectableMethod injectableMethod,
734 Errors errors) {
735 boolean result = true;
736 if (injectableMethod.jsr330) {
737 Method method = injectableMethod.method;
738 if (Modifier.isAbstract(method.getModifiers())) {
739 errors.cannotInjectAbstractMethod(method);
740 result = false;
741 }
742 if (method.getTypeParameters().length > 0) {
743 errors.cannotInjectMethodWithTypeParameters(method);
744 result = false;
745 }
746 }
747 return result;
748 }
749
crazybobleeeedf8162009-09-29 02:57:14 +0000750 private static List<TypeLiteral<?>> hierarchyFor(TypeLiteral<?> type) {
crazybobleeeedf8162009-09-29 02:57:14 +0000751 List<TypeLiteral<?>> hierarchy = new ArrayList<TypeLiteral<?>>();
752 TypeLiteral<?> current = type;
753 while (current.getRawType() != Object.class) {
754 hierarchy.add(current);
755 current = current.getSupertype(current.getRawType().getSuperclass());
756 }
crazybobleeeedf8162009-09-29 02:57:14 +0000757 return hierarchy;
limpbizkit18cb1912008-09-02 18:20:45 +0000758 }
759
crazybobleeeedf8162009-09-29 02:57:14 +0000760 /**
761 * Returns true if a overrides b. Assumes signatures of a and b are the same and a's declaring
762 * class is a subclass of b's declaring class.
763 */
764 private static boolean overrides(Method a, Method b) {
765 // See JLS section 8.4.8.1
crazybobleeeedf8162009-09-29 02:57:14 +0000766 int modifiers = b.getModifiers();
767 if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
768 return true;
769 }
770 if (Modifier.isPrivate(modifiers)) {
771 return false;
772 }
773 // b must be package-private
774 return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
775 }
776
777 /**
778 * A method signature. Used to handle method overridding.
779 */
780 static class Signature {
781
782 final String name;
783 final Class[] parameterTypes;
784 final int hash;
785
786 Signature(Method method) {
787 this.name = method.getName();
788 this.parameterTypes = method.getParameterTypes();
789
790 int h = name.hashCode();
791 h = h * 31 + parameterTypes.length;
792 for (Class parameterType : parameterTypes) {
793 h = h * 31 + parameterType.hashCode();
limpbizkit18cb1912008-09-02 18:20:45 +0000794 }
crazybobleeeedf8162009-09-29 02:57:14 +0000795 this.hash = h;
796 }
797
798 @Override public int hashCode() {
799 return this.hash;
800 }
801
802 @Override public boolean equals(Object o) {
803 if (!(o instanceof Signature)) {
804 return false;
805 }
806
807 Signature other = (Signature) o;
808 if (!name.equals(other.name)) {
809 return false;
810 }
811
812 if (parameterTypes.length != other.parameterTypes.length) {
813 return false;
814 }
815
816 for (int i = 0; i < parameterTypes.length; i++) {
817 if (parameterTypes[i] != other.parameterTypes[i]) {
limpbizkitb47d75a2009-09-08 00:17:45 +0000818 return false;
819 }
limpbizkitb47d75a2009-09-08 00:17:45 +0000820 }
limpbizkit18cb1912008-09-02 18:20:45 +0000821
crazybobleeeedf8162009-09-29 02:57:14 +0000822 return true;
823 }
limpbizkit76a2b9a2008-08-14 04:58:23 +0000824 }
limpbizkit564053f2008-06-15 11:21:18 +0000825}