11package org .codehaus .plexus .interpolation .reflection ;
22
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-
113/*
124 * Copyright 2001-2006 Codehaus Foundation.
135 *
2618
2719import org .codehaus .plexus .interpolation .util .StringUtils ;
2820
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+
2928/**
3029 * <b>NOTE:</b> This class was copied from plexus-utils, to allow this library
3130 * to stand completely self-contained.
@@ -44,317 +43,90 @@ public class ReflectionValueExtractor
4443 private static final Object [] OBJECT_ARGS = new Object [0 ];
4544
4645 /**
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.
4949 */
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 >>();
13451
13552 private ReflectionValueExtractor ()
13653 {
13754 }
13855
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- */
15656 public static Object evaluate ( String expression , Object root )
15757 throws Exception
15858 {
15959 return evaluate ( expression , root , true );
16060 }
16161
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- */
17962 // 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 )
18164 throws Exception
18265 {
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+
18372 Object value = root ;
18473
18574 // ----------------------------------------------------------------------
18675 // Walk the dots and retrieve the ultimate value desired from the
18776 // MavenProject instance.
18877 // ----------------------------------------------------------------------
18978
190- if ( expression == null || "" .equals (expression .trim ()) || !Character .isJavaIdentifierStart ( expression .charAt ( 0 ) ) )
191- {
192- return null ;
193- }
79+ StringTokenizer parser = new StringTokenizer ( expression , "." );
19480
195- boolean hasDots = expression .indexOf ( PROPERTY_START ) >= 0 ;
196-
197- final Tokenizer tokenizer ;
198- if ( trimRootToken && hasDots )
81+ while ( parser .hasMoreTokens () )
19982 {
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 ();
21184
212- int propertyPosition = tokenizer .getPosition ();
213- while ( value != null && tokenizer .peekChar () != EOF )
214- {
215- switch ( tokenizer .skipChar () )
85+ if ( value == null )
21686 {
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 ;
23488 }
235- }
236-
237- return value ;
238- }
23989
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 };
25290 ClassMap classMap = getClassMap ( value .getClass () );
253- Method method = classMap .findMethod ( "get" , localParams );
254- return method .invoke ( value , localParams );
255- }
25691
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 );
26093
261- throw new Exception ( message );
262- }
94+ String methodName = "get" + methodBase ;
26395
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 );
27197
272- if ( value . getClass (). isArray () )
98+ if ( method == null )
27399 {
274- return Array . get ( value , index );
275- }
100+ // perhaps this is a boolean property??
101+ methodName = "is" + methodBase ;
276102
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 );
284104 }
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 )
294107 {
295108 return null ;
296109 }
297110
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 );
314112 }
315113
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 ;
342115 }
343116
344117 private static ClassMap getClassMap ( Class <?> clazz )
345118 {
346-
347- WeakReference <ClassMap > softRef = classMaps .get ( clazz );
119+ WeakReference <ClassMap > ref = classMaps .get ( clazz );
348120
349121 ClassMap classMap ;
350122
351- if ( softRef == null || ( classMap = softRef .get () ) == null )
123+ if ( ref == null || (classMap = ref .get ()) == null )
352124 {
353125 classMap = new ClassMap ( clazz );
354126
355- classMaps .put ( clazz , new WeakReference <ClassMap >( classMap ) );
127+ classMaps .put ( clazz , new WeakReference <ClassMap >(classMap ) );
356128 }
357129
358130 return classMap ;
359131 }
360- }
132+ }
0 commit comments