Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MethodParameter |
|
| 0.0;0 |
1 | /* | |
2 | * $Id: MethodParameter.java 20321 2010-11-24 15:21:24Z dfeist $ | |
3 | * -------------------------------------------------------------------------------------- | |
4 | * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com | |
5 | * | |
6 | * The software in this package is published under the terms of the CPAL v1.0 | |
7 | * license, a copy of which has been included with this distribution in the | |
8 | * LICENSE.txt file. | |
9 | */ | |
10 | package org.mule.util.generics; | |
11 | ||
12 | import java.lang.annotation.Annotation; | |
13 | import java.lang.reflect.Constructor; | |
14 | import java.lang.reflect.Method; | |
15 | import java.lang.reflect.Type; | |
16 | import java.lang.reflect.TypeVariable; | |
17 | import java.util.HashMap; | |
18 | import java.util.Map; | |
19 | ||
20 | /** | |
21 | * Helper class that encapsulates the specification of a method parameter, i.e. | |
22 | * a Method or Constructor plus a parameter index and a nested type index for | |
23 | * a declared generic type. Useful as a specification object to pass along. | |
24 | * <p/> | |
25 | * author: Spring | |
26 | */ | |
27 | public class MethodParameter | |
28 | { | |
29 | ||
30 | private Method method; | |
31 | ||
32 | private Constructor constructor; | |
33 | ||
34 | private final int parameterIndex; | |
35 | ||
36 | private Class<?> parameterType; | |
37 | ||
38 | private Type genericParameterType; | |
39 | ||
40 | private Annotation[] parameterAnnotations; | |
41 | ||
42 | private ParameterNameDiscoverer parameterNameDiscoverer; | |
43 | ||
44 | private String parameterName; | |
45 | ||
46 | 0 | private int nestingLevel = 1; |
47 | ||
48 | /** | |
49 | * Map from Integer level to Integer type index | |
50 | */ | |
51 | private Map<Integer, Integer> typeIndexesPerLevel; | |
52 | ||
53 | Map<TypeVariable, Type> typeVariableMap; | |
54 | ||
55 | ||
56 | /** | |
57 | * Create a new MethodParameter for the given method, with nesting level 1. | |
58 | * | |
59 | * @param method the Method to specify a parameter for | |
60 | * @param parameterIndex the index of the parameter | |
61 | */ | |
62 | public MethodParameter(Method method, int parameterIndex) | |
63 | { | |
64 | 0 | this(method, parameterIndex, 1); |
65 | 0 | } |
66 | ||
67 | /** | |
68 | * Create a new MethodParameter for the given method. | |
69 | * | |
70 | * @param method the Method to specify a parameter for | |
71 | * @param parameterIndex the index of the parameter | |
72 | * (-1 for the method return type; 0 for the first method parameter, | |
73 | * 1 for the second method parameter, etc) | |
74 | * @param nestingLevel the nesting level of the target type | |
75 | * (typically 1; e.g. in case of a List of Lists, 1 would indicate the | |
76 | * nested List, whereas 2 would indicate the element of the nested List) | |
77 | */ | |
78 | public MethodParameter(Method method, int parameterIndex, int nestingLevel) | |
79 | 0 | { |
80 | 0 | this.method = method; |
81 | 0 | this.parameterIndex = parameterIndex; |
82 | 0 | this.nestingLevel = nestingLevel; |
83 | 0 | } |
84 | ||
85 | /** | |
86 | * Create a new MethodParameter for the given constructor, with nesting level 1. | |
87 | * | |
88 | * @param constructor the Constructor to specify a parameter for | |
89 | * @param parameterIndex the index of the parameter | |
90 | */ | |
91 | public MethodParameter(Constructor constructor, int parameterIndex) | |
92 | { | |
93 | 0 | this(constructor, parameterIndex, 1); |
94 | 0 | } |
95 | ||
96 | /** | |
97 | * Create a new MethodParameter for the given constructor. | |
98 | * | |
99 | * @param constructor the Constructor to specify a parameter for | |
100 | * @param parameterIndex the index of the parameter | |
101 | * @param nestingLevel the nesting level of the target type | |
102 | * (typically 1; e.g. in case of a List of Lists, 1 would indicate the | |
103 | * nested List, whereas 2 would indicate the element of the nested List) | |
104 | */ | |
105 | public MethodParameter(Constructor constructor, int parameterIndex, int nestingLevel) | |
106 | 0 | { |
107 | 0 | this.constructor = constructor; |
108 | 0 | this.parameterIndex = parameterIndex; |
109 | 0 | this.nestingLevel = nestingLevel; |
110 | 0 | } |
111 | ||
112 | /** | |
113 | * Copy constructor, resulting in an independent MethodParameter object | |
114 | * based on the same metadata and cache state that the original object was in. | |
115 | * | |
116 | * @param original the original MethodParameter object to copy from | |
117 | */ | |
118 | public MethodParameter(MethodParameter original) | |
119 | 0 | { |
120 | 0 | this.method = original.method; |
121 | 0 | this.constructor = original.constructor; |
122 | 0 | this.parameterIndex = original.parameterIndex; |
123 | 0 | this.parameterType = original.parameterType; |
124 | 0 | this.parameterAnnotations = original.parameterAnnotations; |
125 | 0 | this.typeVariableMap = original.typeVariableMap; |
126 | 0 | } |
127 | ||
128 | ||
129 | /** | |
130 | * Return the wrapped Method, if any. | |
131 | * <p>Note: Either Method or Constructor is available. | |
132 | * | |
133 | * @return the Method, or <code>null</code> if none | |
134 | */ | |
135 | public Method getMethod() | |
136 | { | |
137 | 0 | return this.method; |
138 | } | |
139 | ||
140 | /** | |
141 | * Return the wrapped Constructor, if any. | |
142 | * <p>Note: Either Method or Constructor is available. | |
143 | * | |
144 | * @return the Constructor, or <code>null</code> if none | |
145 | */ | |
146 | public Constructor getConstructor() | |
147 | { | |
148 | 0 | return this.constructor; |
149 | } | |
150 | ||
151 | /** | |
152 | * Return the class that declares the underlying Method or Constructor. | |
153 | */ | |
154 | public Class getDeclaringClass() | |
155 | { | |
156 | 0 | return (this.method != null ? this.method.getDeclaringClass() : this.constructor.getDeclaringClass()); |
157 | } | |
158 | ||
159 | /** | |
160 | * Return the index of the method/constructor parameter. | |
161 | * | |
162 | * @return the parameter index (never negative) | |
163 | */ | |
164 | public int getParameterIndex() | |
165 | { | |
166 | 0 | return this.parameterIndex; |
167 | } | |
168 | ||
169 | /** | |
170 | * Set a resolved (generic) parameter type. | |
171 | */ | |
172 | void setParameterType(Class<?> parameterType) | |
173 | { | |
174 | 0 | this.parameterType = parameterType; |
175 | 0 | } |
176 | ||
177 | /** | |
178 | * Return the type of the method/constructor parameter. | |
179 | * | |
180 | * @return the parameter type (never <code>null</code>) | |
181 | */ | |
182 | public Class<?> getParameterType() | |
183 | { | |
184 | 0 | if (this.parameterType == null) |
185 | { | |
186 | 0 | if (this.parameterIndex < 0) |
187 | { | |
188 | 0 | this.parameterType = (this.method != null ? this.method.getReturnType() : null); |
189 | } | |
190 | else | |
191 | { | |
192 | 0 | this.parameterType = (this.method != null ? |
193 | this.method.getParameterTypes()[this.parameterIndex] : | |
194 | this.constructor.getParameterTypes()[this.parameterIndex]); | |
195 | } | |
196 | } | |
197 | 0 | return this.parameterType; |
198 | } | |
199 | ||
200 | /** | |
201 | * Return the generic type of the method/constructor parameter. | |
202 | * | |
203 | * @return the parameter type (never <code>null</code>) | |
204 | */ | |
205 | public Type getGenericParameterType() | |
206 | { | |
207 | 0 | if (this.genericParameterType == null) |
208 | { | |
209 | 0 | if (this.parameterIndex < 0) |
210 | { | |
211 | 0 | this.genericParameterType = (this.method != null ? this.method.getGenericReturnType() : null); |
212 | } | |
213 | else | |
214 | { | |
215 | 0 | this.genericParameterType = (this.method != null ? |
216 | this.method.getGenericParameterTypes()[this.parameterIndex] : | |
217 | this.constructor.getGenericParameterTypes()[this.parameterIndex]); | |
218 | } | |
219 | } | |
220 | 0 | return this.genericParameterType; |
221 | } | |
222 | ||
223 | /** | |
224 | * Return the annotations associated with the target method/constructor itself. | |
225 | */ | |
226 | public Annotation[] getMethodAnnotations() | |
227 | { | |
228 | 0 | return (this.method != null ? this.method.getAnnotations() : this.constructor.getAnnotations()); |
229 | } | |
230 | ||
231 | /** | |
232 | * Return the method/constructor annotation of the given type, if available. | |
233 | * | |
234 | * @param annotationType the annotation type to look for | |
235 | * @return the annotation object, or <code>null</code> if not found | |
236 | */ | |
237 | @SuppressWarnings("unchecked") | |
238 | public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) | |
239 | { | |
240 | 0 | return (this.method != null ? this.method.getAnnotation(annotationType) : |
241 | (T) this.constructor.getAnnotation(annotationType)); | |
242 | } | |
243 | ||
244 | /** | |
245 | * Return the annotations associated with the specific method/constructor parameter. | |
246 | */ | |
247 | public Annotation[] getParameterAnnotations() | |
248 | { | |
249 | 0 | if (this.parameterAnnotations == null) |
250 | { | |
251 | 0 | Annotation[][] annotationArray = (this.method != null ? |
252 | this.method.getParameterAnnotations() : this.constructor.getParameterAnnotations()); | |
253 | 0 | this.parameterAnnotations = annotationArray[this.parameterIndex]; |
254 | } | |
255 | 0 | return this.parameterAnnotations; |
256 | } | |
257 | ||
258 | /** | |
259 | * Return the parameter annotation of the given type, if available. | |
260 | * | |
261 | * @param annotationType the annotation type to look for | |
262 | * @return the annotation object, or <code>null</code> if not found | |
263 | */ | |
264 | @SuppressWarnings("unchecked") | |
265 | public <T extends Annotation> T getParameterAnnotation(Class<T> annotationType) | |
266 | { | |
267 | 0 | Annotation[] anns = getParameterAnnotations(); |
268 | 0 | for (Annotation ann : anns) |
269 | { | |
270 | 0 | if (annotationType.isInstance(ann)) |
271 | { | |
272 | 0 | return (T) ann; |
273 | } | |
274 | } | |
275 | 0 | return null; |
276 | } | |
277 | ||
278 | /** | |
279 | * Initialize parameter name discovery for this method parameter. | |
280 | * <p>This method does not actually try to retrieve the parameter name at | |
281 | * this point; it just allows discovery to happen when the application calls | |
282 | * {@link #getParameterName()} (if ever). | |
283 | */ | |
284 | public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) | |
285 | { | |
286 | 0 | this.parameterNameDiscoverer = parameterNameDiscoverer; |
287 | 0 | } |
288 | ||
289 | /** | |
290 | * Return the name of the method/constructor parameter. | |
291 | * | |
292 | * @return the parameter name (may be <code>null</code> if no | |
293 | * parameter name metadata is contained in the class file or no | |
294 | * {@link #initParameterNameDiscovery ParameterNameDiscoverer} | |
295 | * has been set to begin with) | |
296 | */ | |
297 | public String getParameterName() | |
298 | { | |
299 | 0 | if (this.parameterNameDiscoverer != null) |
300 | { | |
301 | 0 | String[] parameterNames = (this.method != null ? |
302 | this.parameterNameDiscoverer.getParameterNames(this.method) : | |
303 | this.parameterNameDiscoverer.getParameterNames(this.constructor)); | |
304 | 0 | if (parameterNames != null) |
305 | { | |
306 | 0 | this.parameterName = parameterNames[this.parameterIndex]; |
307 | } | |
308 | 0 | this.parameterNameDiscoverer = null; |
309 | } | |
310 | 0 | return this.parameterName; |
311 | } | |
312 | ||
313 | /** | |
314 | * Increase this parameter's nesting level. | |
315 | * | |
316 | * @see #getNestingLevel() | |
317 | */ | |
318 | public void increaseNestingLevel() | |
319 | { | |
320 | 0 | this.nestingLevel++; |
321 | 0 | } |
322 | ||
323 | /** | |
324 | * Decrease this parameter's nesting level. | |
325 | * | |
326 | * @see #getNestingLevel() | |
327 | */ | |
328 | public void decreaseNestingLevel() | |
329 | { | |
330 | 0 | getTypeIndexesPerLevel().remove(this.nestingLevel); |
331 | 0 | this.nestingLevel--; |
332 | 0 | } |
333 | ||
334 | /** | |
335 | * Return the nesting level of the target type | |
336 | * (typically 1; e.g. in case of a List of Lists, 1 would indicate the | |
337 | * nested List, whereas 2 would indicate the element of the nested List). | |
338 | */ | |
339 | public int getNestingLevel() | |
340 | { | |
341 | 0 | return this.nestingLevel; |
342 | } | |
343 | ||
344 | /** | |
345 | * Set the type index for the current nesting level. | |
346 | * | |
347 | * @param typeIndex the corresponding type index | |
348 | * (or <code>null</code> for the default type index) | |
349 | * @see #getNestingLevel() | |
350 | */ | |
351 | public void setTypeIndexForCurrentLevel(int typeIndex) | |
352 | { | |
353 | 0 | getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex); |
354 | 0 | } |
355 | ||
356 | /** | |
357 | * Return the type index for the current nesting level. | |
358 | * | |
359 | * @return the corresponding type index, or <code>null</code> | |
360 | * if none specified (indicating the default type index) | |
361 | * @see #getNestingLevel() | |
362 | */ | |
363 | public Integer getTypeIndexForCurrentLevel() | |
364 | { | |
365 | 0 | return getTypeIndexForLevel(this.nestingLevel); |
366 | } | |
367 | ||
368 | /** | |
369 | * Return the type index for the specified nesting level. | |
370 | * | |
371 | * @param nestingLevel the nesting level to check | |
372 | * @return the corresponding type index, or <code>null</code> | |
373 | * if none specified (indicating the default type index) | |
374 | */ | |
375 | public Integer getTypeIndexForLevel(int nestingLevel) | |
376 | { | |
377 | 0 | return getTypeIndexesPerLevel().get(nestingLevel); |
378 | } | |
379 | ||
380 | /** | |
381 | * Obtain the (lazily constructed) type-indexes-per-level Map. | |
382 | */ | |
383 | private Map<Integer, Integer> getTypeIndexesPerLevel() | |
384 | { | |
385 | 0 | if (this.typeIndexesPerLevel == null) |
386 | { | |
387 | 0 | this.typeIndexesPerLevel = new HashMap<Integer, Integer>(4); |
388 | } | |
389 | 0 | return this.typeIndexesPerLevel; |
390 | } | |
391 | ||
392 | ||
393 | /** | |
394 | * Create a new MethodParameter for the given method or constructor. | |
395 | * <p>This is a convenience constructor for scenarios where a | |
396 | * Method or Constructor reference is treated in a generic fashion. | |
397 | * | |
398 | * @param methodOrConstructor the Method or Constructor to specify a parameter for | |
399 | * @param parameterIndex the index of the parameter | |
400 | * @return the corresponding MethodParameter instance | |
401 | */ | |
402 | public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex) | |
403 | { | |
404 | 0 | if (methodOrConstructor instanceof Method) |
405 | { | |
406 | 0 | return new MethodParameter((Method) methodOrConstructor, parameterIndex); |
407 | } | |
408 | 0 | else if (methodOrConstructor instanceof Constructor) { |
409 | 0 | return new MethodParameter((Constructor) methodOrConstructor, parameterIndex); |
410 | } | |
411 | else { | |
412 | 0 | throw new IllegalArgumentException( |
413 | "Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor"); | |
414 | } | |
415 | } | |
416 | ||
417 | } |