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