001 package org.apache.maven.project.interpolation;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.model.Model;
023 import org.apache.maven.project.ProjectBuilderConfiguration;
024 import org.apache.maven.project.path.PathTranslator;
025 import org.codehaus.plexus.component.annotations.Component;
026 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
027 import org.codehaus.plexus.interpolation.Interpolator;
028 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
029 import org.codehaus.plexus.interpolation.ValueSource;
030 import org.codehaus.plexus.logging.Logger;
031
032 import java.io.File;
033 import java.lang.reflect.Array;
034 import java.lang.reflect.Field;
035 import java.security.AccessController;
036 import java.security.PrivilegedAction;
037 import java.util.ArrayList;
038 import java.util.Collection;
039 import java.util.LinkedList;
040 import java.util.List;
041 import java.util.Map;
042 import java.util.WeakHashMap;
043
044 @Deprecated
045 @Component( role = ModelInterpolator.class )
046 public class StringSearchModelInterpolator
047 extends AbstractStringBasedModelInterpolator
048 {
049
050 private static final Map<Class<?>, Field[]> fieldsByClass = new WeakHashMap<Class<?>, Field[]>();
051 private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = new WeakHashMap<Class<?>, Boolean>();
052
053 public StringSearchModelInterpolator()
054 {
055 }
056
057 public StringSearchModelInterpolator( PathTranslator pathTranslator )
058 {
059 super( pathTranslator );
060 }
061
062 public Model interpolate( Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled )
063 throws ModelInterpolationException
064 {
065 interpolateObject( model, model, projectDir, config, debugEnabled );
066
067 return model;
068 }
069
070 protected void interpolateObject( Object obj, Model model, File projectDir, ProjectBuilderConfiguration config,
071 boolean debugEnabled )
072 throws ModelInterpolationException
073 {
074 try
075 {
076 List<ValueSource> valueSources = createValueSources( model, projectDir, config );
077 List<InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config );
078
079 InterpolateObjectAction action =
080 new InterpolateObjectAction( obj, valueSources, postProcessors, debugEnabled,
081 this, getLogger() );
082
083 ModelInterpolationException error = AccessController.doPrivileged( action );
084
085 if ( error != null )
086 {
087 throw error;
088 }
089 }
090 finally
091 {
092 getInterpolator().clearAnswers();
093 }
094 }
095
096 protected Interpolator createInterpolator()
097 {
098 StringSearchInterpolator interpolator = new StringSearchInterpolator();
099 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<Object>();
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 = fieldsByClass.get( cls );
165 if ( fields == null )
166 {
167 fields = cls.getDeclaredFields();
168 fieldsByClass.put( cls, fields );
169 }
170
171 for ( int i = 0; i < fields.length; i++ )
172 {
173 Class<?> type = fields[i].getType();
174 if ( isQualifiedForInterpolation( fields[i], type ) )
175 {
176 boolean isAccessible = fields[i].isAccessible();
177 fields[i].setAccessible( true );
178 try
179 {
180 try
181 {
182 if ( String.class == type )
183 {
184 String value = (String) fields[i].get( target );
185 if ( value != null )
186 {
187 String interpolated = modelInterpolator.interpolateInternal( value, valueSources, postProcessors, debugEnabled );
188
189 if ( !interpolated.equals( value ) )
190 {
191 fields[i].set( target, interpolated );
192 }
193 }
194 }
195 else if ( Collection.class.isAssignableFrom( type ) )
196 {
197 Collection<Object> c = (Collection<Object>) fields[i].get( target );
198 if ( c != null && !c.isEmpty() )
199 {
200 List<Object> originalValues = new ArrayList<Object>( c );
201 try
202 {
203 c.clear();
204 }
205 catch ( UnsupportedOperationException e )
206 {
207 if ( debugEnabled && logger != null )
208 {
209 logger.debug( "Skipping interpolation of field: " + fields[i] + " in: "
210 + cls.getName() + "; it is an unmodifiable collection." );
211 }
212 continue;
213 }
214
215 for ( Object value : originalValues )
216 {
217 if ( value != null )
218 {
219 if ( String.class == value.getClass() )
220 {
221 String interpolated =
222 modelInterpolator.interpolateInternal( (String) value,
223 valueSources,
224 postProcessors,
225 debugEnabled );
226
227 if ( !interpolated.equals( value ) )
228 {
229 c.add( interpolated );
230 }
231 else
232 {
233 c.add( value );
234 }
235 }
236 else
237 {
238 c.add( value );
239 if ( value.getClass().isArray() )
240 {
241 evaluateArray( value );
242 }
243 else
244 {
245 interpolationTargets.add( value );
246 }
247 }
248 }
249 else
250 {
251 // add the null back in...not sure what else to do...
252 c.add( value );
253 }
254 }
255 }
256 }
257 else if ( Map.class.isAssignableFrom( type ) )
258 {
259 Map<Object, Object> m = (Map<Object, Object>) fields[i].get( target );
260 if ( m != null && !m.isEmpty() )
261 {
262 for ( Map.Entry<Object, Object> entry : m.entrySet() )
263 {
264 Object value = entry.getValue();
265
266 if ( value != null )
267 {
268 if ( String.class == value.getClass() )
269 {
270 String interpolated =
271 modelInterpolator.interpolateInternal( (String) value,
272 valueSources,
273 postProcessors,
274 debugEnabled );
275
276 if ( !interpolated.equals( value ) )
277 {
278 try
279 {
280 entry.setValue( interpolated );
281 }
282 catch ( UnsupportedOperationException e )
283 {
284 if ( debugEnabled && logger != null )
285 {
286 logger.debug( "Skipping interpolation of field: "
287 + fields[i] + " (key: " + entry.getKey() + ") in: "
288 + cls.getName()
289 + "; it is an unmodifiable collection." );
290 }
291 continue;
292 }
293 }
294 }
295 else
296 {
297 if ( value.getClass().isArray() )
298 {
299 evaluateArray( value );
300 }
301 else
302 {
303 interpolationTargets.add( value );
304 }
305 }
306 }
307 }
308 }
309 }
310 else
311 {
312 Object value = fields[i].get( target );
313 if ( value != null )
314 {
315 if ( fields[i].getType().isArray() )
316 {
317 evaluateArray( value );
318 }
319 else
320 {
321 interpolationTargets.add( value );
322 }
323 }
324 }
325 }
326 catch ( IllegalArgumentException e )
327 {
328 throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i]
329 + " on class: " + cls.getName(), e );
330 }
331 catch ( IllegalAccessException e )
332 {
333 throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i]
334 + " on class: " + cls.getName(), e );
335 }
336 }
337 finally
338 {
339 fields[i].setAccessible( isAccessible );
340 }
341 }
342 }
343
344 traverseObjectWithParents( cls.getSuperclass(), target );
345 }
346 }
347
348 private boolean isQualifiedForInterpolation( Class<?> cls )
349 {
350 return !cls.getPackage().getName().startsWith( "java" );
351 }
352
353 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
354 {
355 if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) )
356 {
357 fieldIsPrimitiveByClass.put( fieldType, Boolean.valueOf( fieldType.isPrimitive() ) );
358 }
359
360 if ( fieldIsPrimitiveByClass.get( fieldType ).booleanValue() )
361 {
362 return false;
363 }
364
365 // if ( fieldType.isPrimitive() )
366 // {
367 // return false;
368 // }
369
370 if ( "parent".equals( field.getName() ) )
371 {
372 return false;
373 }
374
375 return true;
376 }
377
378 private void evaluateArray( Object target )
379 throws ModelInterpolationException
380 {
381 int len = Array.getLength( target );
382 for ( int i = 0; i < len; i++ )
383 {
384 Object value = Array.get( target, i );
385 if ( value != null )
386 {
387 if ( String.class == value.getClass() )
388 {
389 String interpolated =
390 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
391 debugEnabled );
392
393 if ( !interpolated.equals( value ) )
394 {
395 Array.set( target, i, interpolated );
396 }
397 }
398 else
399 {
400 interpolationTargets.add( value );
401 }
402 }
403 }
404 }
405 }
406
407 }