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