001    package org.apache.maven.project.interpolation;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *  http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.maven.model.Model;
023    import org.apache.maven.project.ProjectBuilderConfiguration;
024    import org.apache.maven.project.path.PathTranslator;
025    import org.codehaus.plexus.component.annotations.Component;
026    import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
027    import org.codehaus.plexus.interpolation.Interpolator;
028    import org.codehaus.plexus.interpolation.StringSearchInterpolator;
029    import org.codehaus.plexus.interpolation.ValueSource;
030    import org.codehaus.plexus.logging.Logger;
031    
032    import java.io.File;
033    import java.lang.reflect.Array;
034    import java.lang.reflect.Field;
035    import java.security.AccessController;
036    import java.security.PrivilegedAction;
037    import java.util.ArrayList;
038    import java.util.Collection;
039    import java.util.LinkedList;
040    import java.util.List;
041    import java.util.Map;
042    import java.util.WeakHashMap;
043    
044    @Deprecated
045    @Component( role = ModelInterpolator.class )
046    public class StringSearchModelInterpolator
047        extends AbstractStringBasedModelInterpolator
048    {
049    
050        private static final Map<Class<?>, Field[]> fieldsByClass = new WeakHashMap<Class<?>, Field[]>();
051        private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = new WeakHashMap<Class<?>, Boolean>();
052    
053        public StringSearchModelInterpolator()
054        {
055        }
056    
057        public StringSearchModelInterpolator( PathTranslator pathTranslator )
058        {
059            super( pathTranslator );
060        }
061    
062        public Model interpolate( Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled )
063            throws ModelInterpolationException
064        {
065            interpolateObject( model, model, projectDir, config, debugEnabled );
066    
067            return model;
068        }
069    
070        protected void interpolateObject( Object obj, Model model, File projectDir, ProjectBuilderConfiguration config,
071                                          boolean debugEnabled )
072            throws ModelInterpolationException
073        {
074            try
075            {
076                List<ValueSource> valueSources = createValueSources( model, projectDir, config );
077                List<InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config );
078    
079                InterpolateObjectAction action =
080                    new InterpolateObjectAction( obj, valueSources, postProcessors, debugEnabled,
081                                                 this, getLogger() );
082    
083                ModelInterpolationException error = AccessController.doPrivileged( action );
084    
085                if ( error != null )
086                {
087                    throw error;
088                }
089            }
090            finally
091            {
092                getInterpolator().clearAnswers();
093            }
094        }
095    
096        protected Interpolator createInterpolator()
097        {
098            StringSearchInterpolator interpolator = new StringSearchInterpolator();
099            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<Object>();
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 = fieldsByClass.get( cls );
165                    if ( fields == null )
166                    {
167                        fields = cls.getDeclaredFields();
168                        fieldsByClass.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<Object>( 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 e )
330                                {
331                                    throw new ModelInterpolationException(
332                                        "Failed to interpolate field: " + field + " on class: " + cls.getName(), e );
333                                }
334                                catch ( IllegalAccessException e )
335                                {
336                                    throw new ModelInterpolationException(
337                                        "Failed to interpolate field: " + field + " on class: " + cls.getName(), e );
338                                }
339                            }
340                            finally
341                            {
342                                field.setAccessible( isAccessible );
343                            }
344                        }
345                    }
346    
347                    traverseObjectWithParents( cls.getSuperclass(), target );
348                }
349            }
350    
351            private boolean isQualifiedForInterpolation( Class<?> cls )
352            {
353                return !cls.getPackage().getName().startsWith( "java" );
354            }
355    
356            private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
357            {
358                if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) )
359                {
360                    fieldIsPrimitiveByClass.put( fieldType, fieldType.isPrimitive() );
361                }
362    
363                if ( fieldIsPrimitiveByClass.get( fieldType ).booleanValue() )
364                {
365                    return false;
366                }
367    
368    //            if ( fieldType.isPrimitive() )
369    //            {
370    //                return false;
371    //            }
372    
373                if ( "parent".equals( field.getName() ) )
374                {
375                    return false;
376                }
377    
378                return true;
379            }
380    
381            private void evaluateArray( Object target )
382                throws ModelInterpolationException
383            {
384                int len = Array.getLength( target );
385                for ( int i = 0; i < len; i++ )
386                {
387                    Object value = Array.get( target, i );
388                    if ( value != null )
389                    {
390                        if ( String.class == value.getClass() )
391                        {
392                            String interpolated =
393                                modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
394                                                                       debugEnabled );
395    
396                            if ( !interpolated.equals( value ) )
397                            {
398                                Array.set( target, i, interpolated );
399                            }
400                        }
401                        else
402                        {
403                            interpolationTargets.add( value );
404                        }
405                    }
406                }
407            }
408        }
409    
410    }