View Javadoc

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