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