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