View Javadoc

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