1
1
package org .codehaus .plexus .interpolation .reflection ;
2
2
3
- import java .lang .ref .WeakReference ;
4
- import java .lang .reflect .Array ;
5
- import java .lang .reflect .InvocationTargetException ;
6
- import java .lang .reflect .Method ;
7
- import java .util .List ;
8
- import java .util .Map ;
9
- import java .util .WeakHashMap ;
10
-
11
3
/*
12
4
* Copyright 2001-2006 Codehaus Foundation.
13
5
*
26
18
27
19
import org .codehaus .plexus .interpolation .util .StringUtils ;
28
20
21
+ import java .lang .ref .SoftReference ;
22
+ import java .lang .ref .WeakReference ;
23
+ import java .lang .reflect .Method ;
24
+ import java .util .Map ;
25
+ import java .util .StringTokenizer ;
26
+ import java .util .WeakHashMap ;
27
+
29
28
/**
30
29
* <b>NOTE:</b> This class was copied from plexus-utils, to allow this library
31
30
* to stand completely self-contained.
@@ -44,317 +43,90 @@ public class ReflectionValueExtractor
44
43
private static final Object [] OBJECT_ARGS = new Object [0 ];
45
44
46
45
/**
47
- * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen
48
- * space overflows due to retention of discarded classloaders.
46
+ * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected.
47
+ * This approach prevents permgen space overflows due to retention of discarded
48
+ * classloaders.
49
49
*/
50
- private static final Map <Class <?>, WeakReference <ClassMap >> classMaps =
51
- new WeakHashMap <Class <?>, WeakReference <ClassMap >>();
52
-
53
- static final int EOF = -1 ;
54
-
55
- static final char PROPERTY_START = '.' ;
56
-
57
- static final char INDEXED_START = '[' ;
58
-
59
- static final char INDEXED_END = ']' ;
60
-
61
- static final char MAPPED_START = '(' ;
62
-
63
- static final char MAPPED_END = ')' ;
64
-
65
- static class Tokenizer
66
- {
67
- final String expression ;
68
-
69
- int idx ;
70
-
71
- public Tokenizer ( String expression )
72
- {
73
- this .expression = expression ;
74
- }
75
-
76
- public int peekChar ()
77
- {
78
- return idx < expression .length () ? expression .charAt ( idx ) : EOF ;
79
- }
80
-
81
- public int skipChar ()
82
- {
83
- return idx < expression .length () ? expression .charAt ( idx ++ ) : EOF ;
84
- }
85
-
86
- public String nextToken ( char delimiter )
87
- {
88
- int start = idx ;
89
-
90
- while ( idx < expression .length () && delimiter != expression .charAt ( idx ) )
91
- {
92
- idx ++;
93
- }
94
-
95
- // delimiter MUST be present
96
- if ( idx <= start || idx >= expression .length () )
97
- {
98
- return null ;
99
- }
100
-
101
- return expression .substring ( start , idx ++ );
102
- }
103
-
104
- public String nextPropertyName ()
105
- {
106
- final int start = idx ;
107
-
108
- while ( idx < expression .length () && Character .isJavaIdentifierPart ( expression .charAt ( idx ) ) )
109
- {
110
- idx ++;
111
- }
112
-
113
- // property name does not require delimiter
114
- if ( idx <= start || idx > expression .length () )
115
- {
116
- return null ;
117
- }
118
-
119
- return expression .substring ( start , idx );
120
- }
121
-
122
- public int getPosition ()
123
- {
124
- return idx < expression .length () ? idx : EOF ;
125
- }
126
-
127
- // to make tokenizer look pretty in debugger
128
- @ Override
129
- public String toString ()
130
- {
131
- return idx < expression .length () ? expression .substring ( idx ) : "<EOF>" ;
132
- }
133
- }
50
+ private static final Map <Class <?>, WeakReference <ClassMap >> classMaps = new WeakHashMap <Class <?>, WeakReference <ClassMap >>();
134
51
135
52
private ReflectionValueExtractor ()
136
53
{
137
54
}
138
55
139
- /**
140
- * <p>
141
- * The implementation supports indexed, nested and mapped properties.
142
- * </p>
143
- * <ul>
144
- * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li>
145
- * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code>
146
- * pattern, i.e. "user.addresses[1].street"</li>
147
- * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e.
148
- * "user.addresses(myAddress).street"</li>
149
- * <ul>
150
- *
151
- * @param expression not null expression
152
- * @param root not null object
153
- * @return the object defined by the expression
154
- * @throws Exception if any
155
- */
156
56
public static Object evaluate ( String expression , Object root )
157
57
throws Exception
158
58
{
159
59
return evaluate ( expression , root , true );
160
60
}
161
61
162
- /**
163
- * <p>
164
- * The implementation supports indexed, nested and mapped properties.
165
- * </p>
166
- * <ul>
167
- * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li>
168
- * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code>
169
- * pattern, i.e. "user.addresses[1].street"</li>
170
- * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e.
171
- * "user.addresses(myAddress).street"</li>
172
- * <ul>
173
- *
174
- * @param expression not null expression
175
- * @param root not null object
176
- * @return the object defined by the expression
177
- * @throws Exception if any
178
- */
179
62
// TODO: don't throw Exception
180
- public static Object evaluate ( String expression , final Object root , final boolean trimRootToken )
63
+ public static Object evaluate ( String expression , Object root , boolean trimRootToken )
181
64
throws Exception
182
65
{
66
+ // if the root token refers to the supplied root object parameter, remove it.
67
+ if ( trimRootToken )
68
+ {
69
+ expression = expression .substring ( expression .indexOf ( '.' ) + 1 );
70
+ }
71
+
183
72
Object value = root ;
184
73
185
74
// ----------------------------------------------------------------------
186
75
// Walk the dots and retrieve the ultimate value desired from the
187
76
// MavenProject instance.
188
77
// ----------------------------------------------------------------------
189
78
190
- if ( expression == null || "" .equals (expression .trim ()) || !Character .isJavaIdentifierStart ( expression .charAt ( 0 ) ) )
191
- {
192
- return null ;
193
- }
79
+ StringTokenizer parser = new StringTokenizer ( expression , "." );
194
80
195
- boolean hasDots = expression .indexOf ( PROPERTY_START ) >= 0 ;
196
-
197
- final Tokenizer tokenizer ;
198
- if ( trimRootToken && hasDots )
81
+ while ( parser .hasMoreTokens () )
199
82
{
200
- tokenizer = new Tokenizer ( expression );
201
- tokenizer .nextPropertyName ();
202
- if ( tokenizer .getPosition () == EOF )
203
- {
204
- return null ;
205
- }
206
- }
207
- else
208
- {
209
- tokenizer = new Tokenizer ( "." + expression );
210
- }
83
+ String token = parser .nextToken ();
211
84
212
- int propertyPosition = tokenizer .getPosition ();
213
- while ( value != null && tokenizer .peekChar () != EOF )
214
- {
215
- switch ( tokenizer .skipChar () )
85
+ if ( value == null )
216
86
{
217
- case INDEXED_START :
218
- value =
219
- getIndexedValue ( expression , propertyPosition , tokenizer .getPosition (), value ,
220
- tokenizer .nextToken ( INDEXED_END ) );
221
- break ;
222
- case MAPPED_START :
223
- value =
224
- getMappedValue ( expression , propertyPosition , tokenizer .getPosition (), value ,
225
- tokenizer .nextToken ( MAPPED_END ) );
226
- break ;
227
- case PROPERTY_START :
228
- propertyPosition = tokenizer .getPosition ();
229
- value = getPropertyValue ( value , tokenizer .nextPropertyName () );
230
- break ;
231
- default :
232
- // could not parse expression
233
- return null ;
87
+ return null ;
234
88
}
235
- }
236
-
237
- return value ;
238
- }
239
89
240
- private static Object getMappedValue ( final String expression , final int from , final int to , final Object value ,
241
- final String key )
242
- throws Exception
243
- {
244
- if ( value == null || key == null )
245
- {
246
- return null ;
247
- }
248
-
249
- if ( value instanceof Map )
250
- {
251
- Object [] localParams = new Object [] { key };
252
90
ClassMap classMap = getClassMap ( value .getClass () );
253
- Method method = classMap .findMethod ( "get" , localParams );
254
- return method .invoke ( value , localParams );
255
- }
256
91
257
- final String message =
258
- String .format ( "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'" ,
259
- expression .subSequence ( from , to ), from , value .getClass () );
92
+ String methodBase = StringUtils .capitalizeFirstLetter ( token );
260
93
261
- throw new Exception ( message );
262
- }
94
+ String methodName = "get" + methodBase ;
263
95
264
- private static Object getIndexedValue ( final String expression , final int from , final int to , final Object value ,
265
- final String indexStr )
266
- throws Exception
267
- {
268
- try
269
- {
270
- int index = Integer .parseInt ( indexStr );
96
+ Method method = classMap .findMethod ( methodName , CLASS_ARGS );
271
97
272
- if ( value . getClass (). isArray () )
98
+ if ( method == null )
273
99
{
274
- return Array . get ( value , index );
275
- }
100
+ // perhaps this is a boolean property??
101
+ methodName = "is" + methodBase ;
276
102
277
- if ( value instanceof List )
278
- {
279
- ClassMap classMap = getClassMap ( value .getClass () );
280
- // use get method on List interface
281
- Object [] localParams = new Object [] { index };
282
- Method method = classMap .findMethod ( "get" , localParams );
283
- return method .invoke ( value , localParams );
103
+ method = classMap .findMethod ( methodName , CLASS_ARGS );
284
104
}
285
- }
286
- catch ( NumberFormatException e )
287
- {
288
- return null ;
289
- }
290
- catch ( InvocationTargetException e )
291
- {
292
- // catch array index issues gracefully, otherwise release
293
- if ( e .getCause () instanceof IndexOutOfBoundsException )
105
+
106
+ if ( method == null )
294
107
{
295
108
return null ;
296
109
}
297
110
298
- throw e ;
299
- }
300
-
301
- final String message =
302
- String .format ( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value seems is an instance of '%s'" ,
303
- expression .subSequence ( from , to ), from , value .getClass () );
304
-
305
- throw new Exception ( message );
306
- }
307
-
308
- private static Object getPropertyValue ( Object value , String property )
309
- throws Exception
310
- {
311
- if ( value == null || property == null )
312
- {
313
- return null ;
111
+ value = method .invoke ( value , OBJECT_ARGS );
314
112
}
315
113
316
- ClassMap classMap = getClassMap ( value .getClass () );
317
- String methodBase = StringUtils .capitalizeFirstLetter ( property );
318
- String methodName = "get" + methodBase ;
319
- Method method = classMap .findMethod ( methodName , CLASS_ARGS );
320
-
321
- if ( method == null )
322
- {
323
- // perhaps this is a boolean property??
324
- methodName = "is" + methodBase ;
325
-
326
- method = classMap .findMethod ( methodName , CLASS_ARGS );
327
- }
328
-
329
- if ( method == null )
330
- {
331
- return null ;
332
- }
333
-
334
- try
335
- {
336
- return method .invoke ( value , OBJECT_ARGS );
337
- }
338
- catch ( InvocationTargetException e )
339
- {
340
- throw e ;
341
- }
114
+ return value ;
342
115
}
343
116
344
117
private static ClassMap getClassMap ( Class <?> clazz )
345
118
{
346
-
347
- WeakReference <ClassMap > softRef = classMaps .get ( clazz );
119
+ WeakReference <ClassMap > ref = classMaps .get ( clazz );
348
120
349
121
ClassMap classMap ;
350
122
351
- if ( softRef == null || ( classMap = softRef .get () ) == null )
123
+ if ( ref == null || (classMap = ref .get ()) == null )
352
124
{
353
125
classMap = new ClassMap ( clazz );
354
126
355
- classMaps .put ( clazz , new WeakReference <ClassMap >( classMap ) );
127
+ classMaps .put ( clazz , new WeakReference <ClassMap >(classMap ) );
356
128
}
357
129
358
130
return classMap ;
359
131
}
360
- }
132
+ }
0 commit comments