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