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.InputLocation;
23 import org.apache.maven.model.Model;
24 import org.apache.maven.model.building.ModelBuildingRequest;
25 import org.apache.maven.model.building.ModelProblem.Severity;
26 import org.apache.maven.model.building.ModelProblem.Version;
27 import org.apache.maven.model.building.ModelProblemCollector;
28 import org.apache.maven.model.building.ModelProblemCollectorRequest;
29 import org.codehaus.plexus.interpolation.InterpolationException;
30 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
31 import org.codehaus.plexus.interpolation.RecursionInterceptor;
32 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
33 import org.codehaus.plexus.interpolation.ValueSource;
34
35 import java.io.File;
36 import java.lang.reflect.Array;
37 import java.lang.reflect.Field;
38 import java.lang.reflect.Modifier;
39 import java.security.AccessController;
40 import java.security.PrivilegedAction;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.HashMap;
44 import java.util.LinkedList;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.concurrent.ConcurrentHashMap;
48
49
50
51
52
53 @Deprecated
54 public class StringSearchModelInterpolator
55 extends AbstractStringBasedModelInterpolator
56 {
57 private static final Map<Class<?>, InterpolateObjectAction.CacheItem> CACHED_ENTRIES =
58 new ConcurrentHashMap<>( 80, 0.75f, 2 );
59
60
61 private interface InnerInterpolator
62 {
63 String interpolate( String value );
64 }
65
66 @Override
67 public Model interpolateModel( Model model, File projectDir, ModelBuildingRequest config,
68 ModelProblemCollector problems )
69 {
70 interpolateObject( model, model, projectDir, config, problems );
71 return model;
72 }
73
74 void interpolateObject( Object obj, Model model, File projectDir, ModelBuildingRequest config,
75 ModelProblemCollector problems )
76 {
77 List<? extends ValueSource> valueSources = createValueSources( model, projectDir, config, problems );
78 List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config );
79
80 InnerInterpolator innerInterpolator = createInterpolator( valueSources, postProcessors, problems );
81
82 PrivilegedAction<Object> action = new InterpolateObjectAction( obj, innerInterpolator, problems );
83 AccessController.doPrivileged( action );
84 }
85
86 private InnerInterpolator createInterpolator( List<? extends ValueSource> valueSources,
87 List<? extends InterpolationPostProcessor> postProcessors,
88 final ModelProblemCollector problems )
89 {
90 final Map<String, String> cache = new HashMap<>();
91 final StringSearchInterpolator interpolator = new StringSearchInterpolator();
92 interpolator.setCacheAnswers( true );
93 for ( ValueSource vs : valueSources )
94 {
95 interpolator.addValueSource( vs );
96 }
97 for ( InterpolationPostProcessor postProcessor : postProcessors )
98 {
99 interpolator.addPostProcessor( postProcessor );
100 }
101 final RecursionInterceptor recursionInterceptor = createRecursionInterceptor();
102 return new InnerInterpolator()
103 {
104 @Override
105 public String interpolate( String value )
106 {
107 if ( value != null && value.contains( "${" ) )
108 {
109 String c = cache.get( value );
110 if ( c == null )
111 {
112 try
113 {
114 c = interpolator.interpolate( value, recursionInterceptor );
115 }
116 catch ( InterpolationException e )
117 {
118 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
119 .setMessage( e.getMessage() ).setException( e ) );
120 }
121 cache.put( value, c );
122 }
123 return c;
124 }
125 return value;
126 }
127 };
128 }
129
130 private static final class InterpolateObjectAction
131 implements PrivilegedAction<Object>
132 {
133 private final LinkedList<Object> interpolationTargets;
134
135 private final InnerInterpolator interpolator;
136
137 private final ModelProblemCollector problems;
138
139 InterpolateObjectAction( Object target, InnerInterpolator interpolator, ModelProblemCollector problems )
140 {
141 this.interpolationTargets = new LinkedList<>();
142 interpolationTargets.add( target );
143 this.interpolator = interpolator;
144 this.problems = problems;
145 }
146
147 @Override
148 public Object run()
149 {
150 while ( !interpolationTargets.isEmpty() )
151 {
152 Object obj = interpolationTargets.removeFirst();
153
154 traverseObjectWithParents( obj.getClass(), obj );
155 }
156 return null;
157 }
158
159 private String interpolate( String value )
160 {
161 return interpolator.interpolate( value );
162 }
163
164 private void traverseObjectWithParents( Class<?> cls, Object target )
165 {
166 if ( cls == null )
167 {
168 return;
169 }
170
171 CacheItem cacheEntry = getCacheEntry( cls );
172 if ( cacheEntry.isArray() )
173 {
174 evaluateArray( target, this );
175 }
176 else if ( cacheEntry.isQualifiedForInterpolation )
177 {
178 cacheEntry.interpolate( target, this );
179
180 traverseObjectWithParents( cls.getSuperclass(), target );
181 }
182 }
183
184 private CacheItem getCacheEntry( Class<?> cls )
185 {
186 CacheItem cacheItem = CACHED_ENTRIES.get( cls );
187 if ( cacheItem == null )
188 {
189 cacheItem = new CacheItem( cls );
190 CACHED_ENTRIES.put( cls, cacheItem );
191 }
192 return cacheItem;
193 }
194
195 private static void evaluateArray( Object target, InterpolateObjectAction ctx )
196 {
197 int len = Array.getLength( target );
198 for ( int i = 0; i < len; i++ )
199 {
200 Object value = Array.get( target, i );
201 if ( value != null )
202 {
203 if ( String.class == value.getClass() )
204 {
205 String interpolated = ctx.interpolate( (String) value );
206
207 if ( !interpolated.equals( value ) )
208 {
209 Array.set( target, i, interpolated );
210 }
211 }
212 else
213 {
214 ctx.interpolationTargets.add( value );
215 }
216 }
217 }
218 }
219
220 private static class CacheItem
221 {
222 private final boolean isArray;
223
224 private final boolean isQualifiedForInterpolation;
225
226 private final CacheField[] fields;
227
228 private boolean isQualifiedForInterpolation( Class<?> cls )
229 {
230 Package pkg = cls.getPackage();
231 if ( pkg == null )
232 {
233 return true;
234 }
235 String pkgName = pkg.getName();
236 return !pkgName.startsWith( "java." ) && !pkgName.startsWith( "javax." );
237 }
238
239 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
240 {
241 if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
242 {
243 return false;
244 }
245 if ( InputLocation.class.equals( fieldType ) )
246 {
247 return false;
248 }
249
250
251 if ( fieldType.isPrimitive() )
252 {
253 return false;
254 }
255
256 return !"parent".equals( field.getName() );
257 }
258
259 CacheItem( Class clazz )
260 {
261 this.isQualifiedForInterpolation = isQualifiedForInterpolation( clazz );
262 this.isArray = clazz.isArray();
263 List<CacheField> fields = new ArrayList<>();
264 if ( isQualifiedForInterpolation )
265 {
266 for ( Field currentField : clazz.getDeclaredFields() )
267 {
268 Class<?> type = currentField.getType();
269 if ( isQualifiedForInterpolation( currentField, type ) )
270 {
271 if ( String.class == type )
272 {
273 if ( !Modifier.isFinal( currentField.getModifiers() ) )
274 {
275 fields.add( new StringField( currentField ) );
276 }
277 }
278 else if ( List.class.isAssignableFrom( type ) )
279 {
280 fields.add( new ListField( currentField ) );
281 }
282 else if ( Collection.class.isAssignableFrom( type ) )
283 {
284 throw new RuntimeException(
285 "We dont interpolate into collections, use a list instead" );
286 }
287 else if ( Map.class.isAssignableFrom( type ) )
288 {
289 fields.add( new MapField( currentField ) );
290 }
291 else
292 {
293 fields.add( new ObjectField( currentField ) );
294 }
295 }
296 }
297 }
298 this.fields = fields.toArray( new CacheField[0] );
299 }
300
301 void interpolate( Object target, InterpolateObjectAction interpolateObjectAction )
302 {
303 for ( CacheField field : fields )
304 {
305 field.interpolate( target, interpolateObjectAction );
306 }
307 }
308
309 boolean isArray()
310 {
311 return isArray;
312 }
313 }
314
315 abstract static class CacheField
316 {
317 final Field field;
318
319 CacheField( Field field )
320 {
321 this.field = field;
322 field.setAccessible( true );
323 }
324
325 void interpolate( Object target, InterpolateObjectAction interpolateObjectAction )
326 {
327 try
328 {
329 doInterpolate( target, interpolateObjectAction );
330 }
331 catch ( IllegalArgumentException e )
332 {
333 interpolateObjectAction.problems.add(
334 new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage(
335 "Failed to interpolate field3: " + field + " on class: "
336 + field.getType().getName() ).setException(
337 e ) );
338 }
339 catch ( IllegalAccessException e )
340 {
341 interpolateObjectAction.problems.add(
342 new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage(
343 "Failed to interpolate field4: " + field + " on class: "
344 + field.getType().getName() ).setException( e ) );
345 }
346 }
347
348 abstract void doInterpolate( Object target, InterpolateObjectAction ctx )
349 throws IllegalAccessException;
350 }
351
352 static final class StringField
353 extends CacheField
354 {
355 StringField( Field field )
356 {
357 super( field );
358 }
359
360 @Override
361 void doInterpolate( Object target, InterpolateObjectAction ctx )
362 throws IllegalAccessException
363 {
364 String value = (String) field.get( target );
365 if ( value == null )
366 {
367 return;
368 }
369
370 String interpolated = ctx.interpolate( value );
371
372 if ( interpolated != null && !interpolated.equals( value ) )
373 {
374 field.set( target, interpolated );
375 }
376 }
377 }
378
379 static final class ListField
380 extends CacheField
381 {
382 ListField( Field field )
383 {
384 super( field );
385 }
386
387 @Override
388 void doInterpolate( Object target, InterpolateObjectAction ctx )
389 throws IllegalAccessException
390 {
391 @SuppressWarnings( "unchecked" ) List<Object> c = (List<Object>) field.get( target );
392 if ( c == null )
393 {
394 return;
395 }
396
397 for ( int i = 0, size = c.size(); i < size; i++ )
398 {
399 Object value = c.get( i );
400
401 if ( value != null )
402 {
403 if ( String.class == value.getClass() )
404 {
405 String interpolated = ctx.interpolate( (String) value );
406
407 if ( !interpolated.equals( value ) )
408 {
409 try
410 {
411 c.set( i, interpolated );
412 }
413 catch ( UnsupportedOperationException e )
414 {
415 return;
416 }
417 }
418 }
419 else
420 {
421 if ( value.getClass().isArray() )
422 {
423 evaluateArray( value, ctx );
424 }
425 else
426 {
427 ctx.interpolationTargets.add( value );
428 }
429 }
430 }
431 }
432 }
433 }
434
435 static final class MapField
436 extends CacheField
437 {
438 MapField( Field field )
439 {
440 super( field );
441 }
442
443 @Override
444 void doInterpolate( Object target, InterpolateObjectAction ctx )
445 throws IllegalAccessException
446 {
447 @SuppressWarnings( "unchecked" ) Map<Object, Object> m = (Map<Object, Object>) field.get( target );
448 if ( m == null || m.isEmpty() )
449 {
450 return;
451 }
452
453 for ( Map.Entry<Object, Object> entry : m.entrySet() )
454 {
455 Object value = entry.getValue();
456
457 if ( value == null )
458 {
459 continue;
460 }
461
462 if ( String.class == value.getClass() )
463 {
464 String interpolated = ctx.interpolate( (String) value );
465
466 if ( interpolated != null && !interpolated.equals( value ) )
467 {
468 try
469 {
470 entry.setValue( interpolated );
471 }
472 catch ( UnsupportedOperationException ignore )
473 {
474
475 }
476 }
477 }
478 else if ( value.getClass().isArray() )
479 {
480 evaluateArray( value, ctx );
481 }
482 else
483 {
484 ctx.interpolationTargets.add( value );
485 }
486 }
487 }
488 }
489
490 static final class ObjectField
491 extends CacheField
492 {
493 private final boolean isArray;
494
495 ObjectField( Field field )
496 {
497 super( field );
498 this.isArray = field.getType().isArray();
499 }
500
501 @Override
502 void doInterpolate( Object target, InterpolateObjectAction ctx )
503 throws IllegalAccessException
504 {
505 Object value = field.get( target );
506 if ( value != null )
507 {
508 if ( isArray )
509 {
510 evaluateArray( value, ctx );
511 }
512 else
513 {
514 ctx.interpolationTargets.add( value );
515 }
516 }
517 }
518 }
519 }
520 }