1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.reflect.internal;
21
22 import com.google.common.collect.Lists;
23 import org.grouplens.grapht.InvalidBindingException;
24 import org.grouplens.grapht.reflect.Desire;
25 import org.grouplens.grapht.reflect.InjectionPoint;
26 import org.grouplens.grapht.reflect.Satisfaction;
27 import org.grouplens.grapht.util.ClassProxy;
28 import org.grouplens.grapht.util.Preconditions;
29 import org.grouplens.grapht.util.Types;
30
31 import javax.inject.Inject;
32 import java.io.InvalidObjectException;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectStreamException;
35 import java.io.Serializable;
36 import java.lang.reflect.*;
37 import java.util.*;
38
39
40
41
42
43
44
45
46 public class ReflectionDesire implements Desire, Serializable {
47 private static final long serialVersionUID = -1L;
48
49
50
51
52
53
54
55
56
57 public static List<Desire> getDesires(Class<?> type) {
58 List<Desire> desires = Lists.newArrayList();
59
60 boolean ctorFound = false;
61 for (Constructor<?> ctor: type.getDeclaredConstructors()) {
62 if (ctor.getAnnotation(Inject.class) != null) {
63 if (!ctorFound) {
64 ctorFound = true;
65 for (int i = 0; i < ctor.getParameterTypes().length; i++) {
66 desires.add(new ReflectionDesire(new ConstructorParameterInjectionPoint(ctor, i)));
67 }
68 } else {
69
70 throw new InvalidBindingException(type, "More than one constructor with @Inject is not allowed");
71 }
72 }
73 }
74
75
76
77
78 List<Desire> groupDesires = Lists.newArrayList();
79
80
81 Set<Signature> visitedMethods = new HashSet<Signature>();
82 while(type != null) {
83 for (Method m: type.getDeclaredMethods()) {
84 Signature s = new Signature(m);
85 if (!visitedMethods.contains(s) && m.getAnnotation(Inject.class) != null
86 && !Modifier.isStatic(m.getModifiers())) {
87
88 if (m.getParameterTypes().length > 0) {
89 for (int i = 0; i < m.getParameterTypes().length; i++) {
90 groupDesires.add(new ReflectionDesire(new SetterInjectionPoint(m, i)));
91 }
92 } else {
93
94 groupDesires.add(new ReflectionDesire(new NoArgumentInjectionPoint(m)));
95 }
96 }
97
98
99 visitedMethods.add(s);
100 }
101 for (Field f: type.getDeclaredFields()) {
102 if (f.getAnnotation(Inject.class) != null && !Modifier.isStatic(f.getModifiers())) {
103
104 groupDesires.add(new ReflectionDesire(new FieldInjectionPoint(f)));
105 }
106 }
107
108 type = type.getSuperclass();
109 }
110
111
112
113 Collections.reverse(groupDesires);
114 desires.addAll(groupDesires);
115
116 return Collections.unmodifiableList(desires);
117 }
118
119 private final transient Class<?> desiredType;
120 private final transient InjectionPoint injectPoint;
121 private final transient Satisfaction satisfaction;
122
123
124
125
126
127
128
129
130
131
132 public ReflectionDesire(InjectionPoint injectPoint) {
133 this(injectPoint.getErasedType(), injectPoint, null);
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 public ReflectionDesire(Class<?> desiredType, InjectionPoint injectPoint,
152 Satisfaction satisfaction) {
153 Preconditions.notNull("desired type", desiredType);
154 Preconditions.notNull("injection point", injectPoint);
155
156 desiredType = Types.box(desiredType);
157 Preconditions.isAssignable(injectPoint.getErasedType(), desiredType);
158 if (satisfaction != null) {
159 Preconditions.isAssignable(desiredType, satisfaction.getErasedType());
160 }
161
162
163 if (satisfaction == null) {
164 if (Types.shouldBeInstantiable(desiredType)) {
165 if (Types.isInstantiable(desiredType)) {
166 satisfaction = new ClassSatisfaction(desiredType);
167 }
168 } else if (injectPoint.isNullable()) {
169
170
171
172 satisfaction = new NullSatisfaction(desiredType);
173 }
174 }
175
176 this.desiredType = desiredType;
177 this.injectPoint = injectPoint;
178 this.satisfaction = satisfaction;
179 }
180
181 @Override
182 public Class<?> getDesiredType() {
183 return desiredType;
184 }
185
186 @Override
187 public InjectionPoint getInjectionPoint() {
188 return injectPoint;
189 }
190
191 @Override
192 public boolean isInstantiable() {
193 return satisfaction != null;
194 }
195
196 @Override
197 public Satisfaction getSatisfaction() {
198 return satisfaction;
199 }
200
201 @Override
202 public Desire restrict(Class<?> type) {
203 return new ReflectionDesire(type, injectPoint, null);
204 }
205
206 @Override
207 public Desire restrict(Satisfaction satis) {
208 return new ReflectionDesire(satis.getErasedType(), injectPoint, satis);
209 }
210
211 @Override
212 public boolean equals(Object o) {
213 if (!(o instanceof ReflectionDesire)) {
214 return false;
215 }
216 ReflectionDesire r = (ReflectionDesire) o;
217 return (r.desiredType.equals(desiredType) &&
218 r.injectPoint.equals(injectPoint) &&
219 (r.satisfaction == null ? satisfaction == null : r.satisfaction.equals(satisfaction)));
220 }
221
222 @Override
223 public int hashCode() {
224 return desiredType.hashCode() ^ injectPoint.hashCode() ^ (satisfaction == null ? 0 : satisfaction.hashCode());
225 }
226
227 @Override
228 public String toString() {
229 return "Desire(" + desiredType.getSimpleName() + ", " + injectPoint + ")";
230 }
231
232 private Object writeReplace() {
233 return new SerialProxy(desiredType, injectPoint, satisfaction);
234 }
235
236 private void readObject(ObjectInputStream stream) throws ObjectStreamException {
237 throw new InvalidObjectException("must use serialization proxy");
238 }
239
240 private static class SerialProxy implements Serializable {
241 private static final long serialVersionUID = 1L;
242
243 private final InjectionPoint injectionPoint;
244 private final ClassProxy desiredType;
245 private final Satisfaction satisfaction;
246
247 public SerialProxy(Class<?> type, InjectionPoint ip, Satisfaction sat) {
248 injectionPoint = ip;
249 desiredType = ClassProxy.of(type);
250 satisfaction = sat;
251 }
252
253 @SuppressWarnings("unchecked")
254 private Object readResolve() throws ObjectStreamException {
255 try {
256 return new ReflectionDesire(desiredType.resolve(),
257 injectionPoint,
258 satisfaction);
259 } catch (ClassNotFoundException e) {
260 InvalidObjectException ex = new InvalidObjectException("cannot resolve " + desiredType);
261 ex.initCause(e);
262 throw ex;
263 } catch (InvalidBindingException e) {
264 InvalidObjectException ex = new InvalidObjectException("invalid binding");
265 ex.initCause(e);
266 throw ex;
267 }
268 }
269 }
270
271
272
273
274
275
276
277 public static class Signature {
278 private final String name;
279 private final Type[] args;
280
281 public Signature(Method m) {
282
283 int mods = m.getModifiers();
284 if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) {
285
286 name = m.getName();
287 } else if (Modifier.isPrivate(mods)) {
288
289 name = m.getName() + m.getDeclaringClass().getCanonicalName();
290 } else {
291
292
293 Package pkg = m.getDeclaringClass().getPackage();
294 if (pkg != null) {
295 name = m.getName() + pkg.getName();
296 } else {
297 name = m.getName();
298 }
299 }
300 args = m.getGenericParameterTypes();
301 }
302
303 @Override
304 public boolean equals(Object o) {
305 if (!(o instanceof Signature)) {
306 return false;
307 }
308 Signature s = (Signature) o;
309 return s.name.equals(name) && Arrays.equals(args, s.args);
310 }
311
312 @Override
313 public int hashCode() {
314 return (name.hashCode() ^ Arrays.hashCode(args));
315 }
316 }
317 }