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