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 implements PrivilegedAction<Object>
92 {
93
94 private final LinkedList<Object> interpolationTargets;
95 private final StringSearchModelInterpolator modelInterpolator;
96 private final List<? extends ValueSource> valueSources;
97 private final List<? extends InterpolationPostProcessor> postProcessors;
98 private final ModelProblemCollector problems;
99
100 public InterpolateObjectAction( Object target, List<? extends ValueSource> valueSources,
101 List<? extends InterpolationPostProcessor> postProcessors,
102 StringSearchModelInterpolator modelInterpolator,
103 ModelProblemCollector problems )
104 {
105 this.valueSources = valueSources;
106 this.postProcessors = postProcessors;
107
108 this.interpolationTargets = new LinkedList<Object>();
109 interpolationTargets.add( target );
110
111 this.modelInterpolator = modelInterpolator;
112
113 this.problems = problems;
114 }
115
116 public Object run()
117 {
118 while ( !interpolationTargets.isEmpty() )
119 {
120 Object obj = interpolationTargets.removeFirst();
121
122 traverseObjectWithParents( obj.getClass(), obj );
123 }
124
125 return null;
126 }
127
128 @SuppressWarnings( "unchecked" )
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 boolean isAccessible = currentField.isAccessible();
150 currentField.setAccessible( true );
151 try
152 {
153 if ( String.class == type )
154 {
155 String value = (String) currentField.get( target );
156 if ( value != null && !Modifier.isFinal( currentField.getModifiers() ) )
157 {
158 String interpolated =
159 modelInterpolator.interpolateInternal( value, valueSources, postProcessors,
160 problems );
161
162 if ( !interpolated.equals( value ) )
163 {
164 currentField.set( target, interpolated );
165 }
166 }
167 }
168 else if ( Collection.class.isAssignableFrom( type ) )
169 {
170 Collection<Object> c = (Collection<Object>) currentField.get( target );
171 if ( c != null && !c.isEmpty() )
172 {
173 List<Object> originalValues = new ArrayList<Object>( c );
174 try
175 {
176 c.clear();
177 }
178 catch ( UnsupportedOperationException e )
179 {
180 continue;
181 }
182
183 for ( Object value : originalValues )
184 {
185 if ( value != null )
186 {
187 if ( String.class == value.getClass() )
188 {
189 String interpolated =
190 modelInterpolator.interpolateInternal( (String) value,
191 valueSources,
192 postProcessors,
193 problems );
194
195 if ( !interpolated.equals( value ) )
196 {
197 c.add( interpolated );
198 }
199 else
200 {
201 c.add( value );
202 }
203 }
204 else
205 {
206 c.add( value );
207 if ( value.getClass().isArray() )
208 {
209 evaluateArray( value );
210 }
211 else
212 {
213 interpolationTargets.add( value );
214 }
215 }
216 }
217 else
218 {
219
220 c.add( value );
221 }
222 }
223 }
224 }
225 else if ( Map.class.isAssignableFrom( type ) )
226 {
227 Map<Object, Object> m = (Map<Object, Object>) currentField.get( target );
228 if ( m != null && !m.isEmpty() )
229 {
230 for ( Map.Entry<Object, Object> entry : m.entrySet() )
231 {
232 Object value = entry.getValue();
233
234 if ( value != null )
235 {
236 if ( String.class == value.getClass() )
237 {
238 String interpolated =
239 modelInterpolator.interpolateInternal( (String) value,
240 valueSources,
241 postProcessors,
242 problems );
243
244 if ( !interpolated.equals( value ) )
245 {
246 try
247 {
248 entry.setValue( interpolated );
249 }
250 catch ( UnsupportedOperationException e )
251 {
252 continue;
253 }
254 }
255 }
256 else
257 {
258 if ( value.getClass().isArray() )
259 {
260 evaluateArray( value );
261 }
262 else
263 {
264 interpolationTargets.add( value );
265 }
266 }
267 }
268 }
269 }
270 }
271 else
272 {
273 Object value = currentField.get( target );
274 if ( value != null )
275 {
276 if ( currentField.getType().isArray() )
277 {
278 evaluateArray( value );
279 }
280 else
281 {
282 interpolationTargets.add( value );
283 }
284 }
285 }
286 }
287 catch ( IllegalArgumentException e )
288 {
289 problems.add( Severity.ERROR, "Failed to interpolate field3: " + currentField
290 + " on class: " + cls.getName(), null, e );
291 }
292 catch ( IllegalAccessException e )
293 {
294 problems.add( Severity.ERROR, "Failed to interpolate field4: " + currentField
295 + " on class: " + cls.getName(), null, e );
296 }
297 finally
298 {
299 currentField.setAccessible( isAccessible );
300 }
301 }
302 }
303 }
304
305 traverseObjectWithParents( cls.getSuperclass(), target );
306 }
307 }
308
309 private Field[] getFields( Class<?> cls )
310 {
311 Field[] fields = fieldsByClass.get( cls );
312 if ( fields == null )
313 {
314 fields = cls.getDeclaredFields();
315 fieldsByClass.put( cls, fields );
316 }
317 return fields;
318 }
319
320 private boolean isQualifiedForInterpolation( Class<?> cls )
321 {
322 return !cls.getName().startsWith( "java" );
323 }
324
325 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
326 {
327 if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
328 {
329 return false;
330 }
331
332 Boolean primitive = fieldIsPrimitiveByClass.get( fieldType );
333 if ( primitive == null )
334 {
335 primitive = fieldType.isPrimitive();
336 fieldIsPrimitiveByClass.put( fieldType, primitive );
337 }
338
339 if ( primitive )
340 {
341 return false;
342 }
343
344 return !"parent".equals( field.getName() );
345 }
346
347 private void evaluateArray( Object target )
348 {
349 int len = Array.getLength( target );
350 for ( int i = 0; i < len; i++ )
351 {
352 Object value = Array.get( target, i );
353 if ( value != null )
354 {
355 if ( String.class == value.getClass() )
356 {
357 String interpolated =
358 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
359 problems );
360
361 if ( !interpolated.equals( value ) )
362 {
363 Array.set( target, i, interpolated );
364 }
365 }
366 else
367 {
368 interpolationTargets.add( value );
369 }
370 }
371 }
372 }
373 }
374
375 }