1 package org.apache.maven.model.interpolation;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.model.Model;
23 import org.apache.maven.model.building.ModelBuildingRequest;
24 import org.apache.maven.model.building.ModelProblem.Severity;
25 import org.apache.maven.model.building.ModelProblemCollector;
26 import org.codehaus.plexus.component.annotations.Component;
27 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
28 import org.codehaus.plexus.interpolation.Interpolator;
29 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
30 import org.codehaus.plexus.interpolation.ValueSource;
31
32 import java.io.File;
33 import java.lang.reflect.Array;
34 import java.lang.reflect.Field;
35 import java.lang.reflect.Modifier;
36 import java.security.AccessController;
37 import java.security.PrivilegedAction;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.concurrent.ConcurrentHashMap;
44
45 @Component( role = ModelInterpolator.class )
46 public class StringSearchModelInterpolator
47 extends AbstractStringBasedModelInterpolator
48 {
49
50 private static final Map<Class<?>, Field[]> fieldsByClass =
51 new ConcurrentHashMap<Class<?>, Field[]>( 80, 0.75f, 2 );
52 private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass =
53 new ConcurrentHashMap<Class<?>, Boolean>( 62, 0.75f, 2 );
54
55 public Model interpolateModel( Model model, File projectDir, ModelBuildingRequest config,
56 ModelProblemCollector problems )
57 {
58 interpolateObject( model, model, projectDir, config, problems );
59
60 return model;
61 }
62
63 protected void interpolateObject( Object obj, Model model, File projectDir, ModelBuildingRequest config,
64 ModelProblemCollector problems )
65 {
66 try
67 {
68 List<? extends ValueSource> valueSources = createValueSources( model, projectDir, config, problems );
69 List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir,
70 config );
71
72 InterpolateObjectAction action =
73 new InterpolateObjectAction( obj, valueSources, postProcessors, this, problems );
74
75 AccessController.doPrivileged( action );
76 }
77 finally
78 {
79 getInterpolator().clearAnswers();
80 }
81 }
82
83 protected Interpolator createInterpolator()
84 {
85 StringSearchInterpolator interpolator = new StringSearchInterpolator();
86 interpolator.setCacheAnswers( true );
87
88 return interpolator;
89 }
90
91 private static final class InterpolateObjectAction
92 implements PrivilegedAction<Object>
93 {
94
95 private final LinkedList<Object> interpolationTargets;
96 private final StringSearchModelInterpolator modelInterpolator;
97 private final List<? extends ValueSource> valueSources;
98 private final List<? extends InterpolationPostProcessor> postProcessors;
99 private final ModelProblemCollector problems;
100
101 public InterpolateObjectAction( Object target, List<? extends ValueSource> valueSources,
102 List<? extends InterpolationPostProcessor> postProcessors,
103 StringSearchModelInterpolator modelInterpolator,
104 ModelProblemCollector problems )
105 {
106 this.valueSources = valueSources;
107 this.postProcessors = postProcessors;
108
109 this.interpolationTargets = new LinkedList<Object>();
110 interpolationTargets.add( target );
111
112 this.modelInterpolator = modelInterpolator;
113
114 this.problems = problems;
115 }
116
117 public Object run()
118 {
119 while ( !interpolationTargets.isEmpty() )
120 {
121 Object obj = interpolationTargets.removeFirst();
122
123 traverseObjectWithParents( obj.getClass(), obj );
124 }
125
126 return null;
127 }
128
129 private void traverseObjectWithParents( Class<?> cls, Object target )
130 {
131 if ( cls == null )
132 {
133 return;
134 }
135
136 if ( cls.isArray() )
137 {
138 evaluateArray( target );
139 }
140 else if ( isQualifiedForInterpolation( cls ) )
141 {
142 for ( Field currentField : getFields( cls ) )
143 {
144 Class<?> type = currentField.getType();
145 if ( isQualifiedForInterpolation( currentField, type ) )
146 {
147 synchronized ( currentField )
148 {
149 interpolateField( cls, target, currentField, type );
150 }
151 }
152 }
153
154 traverseObjectWithParents( cls.getSuperclass(), target );
155 }
156 }
157
158 private void interpolateField( Class<?> cls, Object target, Field field, Class<?> type )
159 {
160 boolean isAccessible = field.isAccessible();
161 field.setAccessible( true );
162 try
163 {
164 if ( String.class == type )
165 {
166 interpolateStringField( target, field );
167 }
168 else if ( Collection.class.isAssignableFrom( type ) )
169 {
170 interpolateCollectionField( target, field );
171 }
172 else if ( Map.class.isAssignableFrom( type ) )
173 {
174 interpolateMapField( target, field );
175 }
176 else
177 {
178 Object value = field.get( target );
179 if ( value != null )
180 {
181 if ( field.getType().isArray() )
182 {
183 evaluateArray( value );
184 }
185 else
186 {
187 interpolationTargets.add( value );
188 }
189 }
190 }
191 }
192 catch ( IllegalArgumentException e )
193 {
194 problems.add( Severity.ERROR, "Failed to interpolate field3: " + field + " on class: " + cls.getName(),
195 null, e );
196 }
197 catch ( IllegalAccessException e )
198 {
199 problems.add( Severity.ERROR, "Failed to interpolate field4: " + field + " on class: " + cls.getName(),
200 null, e );
201 }
202 finally
203 {
204 field.setAccessible( isAccessible );
205 }
206 }
207
208 private void interpolateStringField( Object target, Field field )
209 throws IllegalAccessException
210 {
211 String value = (String) field.get( target );
212 if ( value == null || Modifier.isFinal( field.getModifiers() ) )
213 {
214 return;
215 }
216
217 String interpolated =
218 modelInterpolator.interpolateInternal( value, valueSources, postProcessors, problems );
219
220 if ( !interpolated.equals( value ) )
221 {
222 field.set( target, interpolated );
223 }
224 }
225
226 private void interpolateCollectionField( Object target, Field field )
227 throws IllegalAccessException
228 {
229 @SuppressWarnings( "unchecked" )
230 Collection<Object> c = (Collection<Object>) field.get( target );
231 if ( c == null || c.isEmpty() )
232 {
233 return;
234 }
235
236 List<Object> originalValues = new ArrayList<Object>( c );
237 try
238 {
239 c.clear();
240 }
241 catch ( UnsupportedOperationException e )
242 {
243 return;
244 }
245
246 for ( Object value : originalValues )
247 {
248 if ( value == null )
249 {
250
251 c.add( value );
252 }
253 else if ( String.class == value.getClass() )
254 {
255 String interpolated =
256 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, problems );
257
258 if ( !interpolated.equals( value ) )
259 {
260 c.add( interpolated );
261 }
262 else
263 {
264 c.add( value );
265 }
266 }
267 else
268 {
269 c.add( value );
270 if ( value.getClass().isArray() )
271 {
272 evaluateArray( value );
273 }
274 else
275 {
276 interpolationTargets.add( value );
277 }
278 }
279 }
280 }
281
282 private void interpolateMapField( Object target, Field field )
283 throws IllegalAccessException
284 {
285 @SuppressWarnings( "unchecked" )
286 Map<Object, Object> m = (Map<Object, Object>) field.get( target );
287 if ( m == null || m.isEmpty() )
288 {
289 return;
290 }
291
292 for ( Map.Entry<Object, Object> entry : m.entrySet() )
293 {
294 Object value = entry.getValue();
295
296 if ( value == null )
297 {
298 continue;
299 }
300
301 if ( String.class == value.getClass() )
302 {
303 String interpolated =
304 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, problems );
305
306 if ( !interpolated.equals( value ) )
307 {
308 try
309 {
310 entry.setValue( interpolated );
311 }
312 catch ( UnsupportedOperationException e )
313 {
314 continue;
315 }
316 }
317 }
318 else if ( value.getClass().isArray() )
319 {
320 evaluateArray( value );
321 }
322 else
323 {
324 interpolationTargets.add( value );
325 }
326 }
327 }
328
329 private Field[] getFields( Class<?> cls )
330 {
331 Field[] fields = fieldsByClass.get( cls );
332 if ( fields == null )
333 {
334 fields = cls.getDeclaredFields();
335 fieldsByClass.put( cls, fields );
336 }
337 return fields;
338 }
339
340 private boolean isQualifiedForInterpolation( Class<?> cls )
341 {
342 return !cls.getName().startsWith( "java" );
343 }
344
345 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
346 {
347 if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
348 {
349 return false;
350 }
351
352 Boolean primitive = fieldIsPrimitiveByClass.get( fieldType );
353 if ( primitive == null )
354 {
355 primitive = fieldType.isPrimitive();
356 fieldIsPrimitiveByClass.put( fieldType, primitive );
357 }
358
359 if ( primitive )
360 {
361 return false;
362 }
363
364 return !"parent".equals( field.getName() );
365 }
366
367 private void evaluateArray( Object target )
368 {
369 int len = Array.getLength( target );
370 for ( int i = 0; i < len; i++ )
371 {
372 Object value = Array.get( target, i );
373 if ( value != null )
374 {
375 if ( String.class == value.getClass() )
376 {
377 String interpolated =
378 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
379 problems );
380
381 if ( !interpolated.equals( value ) )
382 {
383 Array.set( target, i, interpolated );
384 }
385 }
386 else
387 {
388 interpolationTargets.add( value );
389 }
390 }
391 }
392 }
393 }
394
395 }