1 package org.apache.maven.shared.utils.introspection;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.StringTokenizer;
28 import java.util.WeakHashMap;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import org.apache.maven.shared.utils.StringUtils;
32 import org.apache.maven.shared.utils.introspection.MethodMap.AmbiguousException;
33
34 import javax.annotation.Nonnull;
35 import javax.annotation.Nullable;
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class ReflectionValueExtractor
50 {
51 private static final Class<?>[] CLASS_ARGS = new Class[0];
52
53 private static final Object[] OBJECT_ARGS = new Object[0];
54
55
56
57
58
59
60 private static final Map<Class<?>, ClassMap> classMaps = new WeakHashMap<Class<?>, ClassMap>();
61
62
63
64
65 private static final Pattern INDEXED_PROPS = Pattern.compile( "(\\w+)\\[(\\d+)\\]" );
66
67
68
69
70 private static final Pattern MAPPED_PROPS = Pattern.compile( "(\\w+)\\((.+)\\)" );
71
72 private ReflectionValueExtractor()
73 {
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public static Object evaluate( @Nonnull String expression, @Nullable Object root )
92 throws IntrospectionException
93 {
94 return evaluate( expression, root, true );
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 public static Object evaluate( @Nonnull String expression, @Nullable Object root, boolean trimRootToken )
113 throws IntrospectionException
114 {
115
116 if ( trimRootToken )
117 {
118 expression = expression.substring( expression.indexOf( '.' ) + 1 );
119 }
120
121 Object value = root;
122
123
124
125
126
127
128 StringTokenizer parser = new StringTokenizer( expression, "." );
129
130 while ( parser.hasMoreTokens() )
131 {
132
133 if ( value == null )
134 {
135 return null;
136 }
137
138 String token = parser.nextToken();
139
140 ClassMap classMap = getClassMap( value.getClass() );
141
142 Method method;
143 Object[] localParams = OBJECT_ARGS;
144
145
146 Matcher matcher = INDEXED_PROPS.matcher( token );
147 if ( matcher.find() )
148 {
149 String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) );
150 String methodName = "get" + methodBase;
151 try
152 {
153 method = classMap.findMethod( methodName, CLASS_ARGS );
154 }
155 catch ( AmbiguousException e )
156 {
157 throw new IntrospectionException( e );
158 }
159
160 try
161 {
162 value = method.invoke( value, OBJECT_ARGS );
163 }
164 catch ( IllegalArgumentException e )
165 {
166 throw new IntrospectionException( e );
167 }
168 catch ( IllegalAccessException e )
169 {
170 throw new IntrospectionException( e );
171 }
172 catch ( InvocationTargetException e )
173 {
174 throw new IntrospectionException( e );
175 }
176
177 classMap = getClassMap( value.getClass() );
178
179 if ( classMap.getCachedClass().isArray() )
180 {
181 value = Arrays.asList( (Object[]) value );
182 classMap = getClassMap( value.getClass() );
183 }
184
185 if ( value instanceof List )
186 {
187
188 localParams = new Object[1];
189 localParams[0] = Integer.valueOf( matcher.group( 2 ) );
190 try
191 {
192 method = classMap.findMethod( "get", localParams );
193 }
194 catch ( AmbiguousException e )
195 {
196 throw new IntrospectionException( e );
197 }
198 }
199 else
200 {
201 throw new IntrospectionException( "The token '" + token
202 + "' refers to a java.util.List or an array, but the value seems is an instance of '"
203 + value.getClass() + "'." );
204 }
205 }
206 else
207 {
208
209 matcher = MAPPED_PROPS.matcher( token );
210 if ( matcher.find() )
211 {
212 String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) );
213 String methodName = "get" + methodBase;
214 try
215 {
216 method = classMap.findMethod( methodName, CLASS_ARGS );
217 }
218 catch ( AmbiguousException e )
219 {
220 throw new IntrospectionException( e );
221 }
222
223 try
224 {
225 value = method.invoke( value, OBJECT_ARGS );
226 }
227 catch ( IllegalArgumentException e )
228 {
229 throw new IntrospectionException( e );
230 }
231 catch ( IllegalAccessException e )
232 {
233 throw new IntrospectionException( e );
234 }
235 catch ( InvocationTargetException e )
236 {
237 throw new IntrospectionException( e );
238 }
239 classMap = getClassMap( value.getClass() );
240
241 if ( value instanceof Map )
242 {
243
244 localParams = new Object[1];
245 localParams[0] = matcher.group( 2 );
246 try
247 {
248 method = classMap.findMethod( "get", localParams );
249 }
250 catch ( AmbiguousException e )
251 {
252 throw new IntrospectionException( e );
253 }
254 }
255 else
256 {
257 throw new IntrospectionException( "The token '" + token
258 + "' refers to a java.util.Map, but the value seems is an instance of '"
259 + value.getClass() + "'." );
260 }
261 }
262 else
263 {
264 String methodBase = StringUtils.capitalizeFirstLetter( token );
265 String methodName = "get" + methodBase;
266 try
267 {
268 method = classMap.findMethod( methodName, CLASS_ARGS );
269 }
270 catch ( AmbiguousException e )
271 {
272 throw new IntrospectionException( e );
273 }
274
275 if ( method == null )
276 {
277
278 methodName = "is" + methodBase;
279
280 try
281 {
282 method = classMap.findMethod( methodName, CLASS_ARGS );
283 }
284 catch ( AmbiguousException e )
285 {
286 throw new IntrospectionException( e );
287 }
288 }
289 }
290 }
291
292 if ( method == null )
293 {
294 return null;
295 }
296
297 try
298 {
299 value = method.invoke( value, localParams );
300 }
301 catch ( InvocationTargetException e )
302 {
303
304 if ( e.getCause() instanceof IndexOutOfBoundsException )
305 {
306 return null;
307 }
308
309 throw new IntrospectionException( e );
310 }
311 catch ( IllegalArgumentException e )
312 {
313 throw new IntrospectionException( e );
314 }
315 catch ( IllegalAccessException e )
316 {
317 throw new IntrospectionException( e );
318 }
319 }
320
321 return value;
322 }
323
324 private static ClassMap getClassMap( Class<?> clazz )
325 {
326 ClassMap classMap = classMaps.get( clazz );
327
328 if ( classMap == null )
329 {
330 classMap = new ClassMap( clazz );
331
332 classMaps.put( clazz, classMap );
333 }
334
335 return classMap;
336 }
337 }