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