View Javadoc

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