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 ( int i = 0; i < fields.length; i++ )
172                    {
173                        Class<?> type = fields[i].getType();
174                        if ( isQualifiedForInterpolation( fields[i], type ) )
175                        {
176                            boolean isAccessible = fields[i].isAccessible();
177                            fields[i].setAccessible( true );
178                            try
179                            {
180                                try
181                                {
182                                    if ( String.class == type )
183                                    {
184                                        String value = (String) fields[i].get( target );
185                                        if ( value != null )
186                                        {
187                                            String interpolated = modelInterpolator.interpolateInternal( value, valueSources, postProcessors, debugEnabled );
188    
189                                            if ( !interpolated.equals( value ) )
190                                            {
191                                                fields[i].set( target, interpolated );
192                                            }
193                                        }
194                                    }
195                                    else if ( Collection.class.isAssignableFrom( type ) )
196                                    {
197                                        Collection<Object> c = (Collection<Object>) fields[i].get( target );
198                                        if ( c != null && !c.isEmpty() )
199                                        {
200                                            List<Object> originalValues = new ArrayList<Object>( c );
201                                            try
202                                            {
203                                                c.clear();
204                                            }
205                                            catch ( UnsupportedOperationException e )
206                                            {
207                                                if ( debugEnabled && logger != null )
208                                                {
209                                                    logger.debug( "Skipping interpolation of field: " + fields[i] + " in: "
210                                                        + cls.getName() + "; it is an unmodifiable collection." );
211                                                }
212                                                continue;
213                                            }
214    
215                                            for ( Object value : originalValues )
216                                            {
217                                                if ( value != null )
218                                                {
219                                                    if ( String.class == value.getClass() )
220                                                    {
221                                                        String interpolated =
222                                                            modelInterpolator.interpolateInternal( (String) value,
223                                                                                                   valueSources,
224                                                                                                   postProcessors,
225                                                                                                   debugEnabled );
226    
227                                                        if ( !interpolated.equals( value ) )
228                                                        {
229                                                            c.add( interpolated );
230                                                        }
231                                                        else
232                                                        {
233                                                            c.add( value );
234                                                        }
235                                                    }
236                                                    else
237                                                    {
238                                                        c.add( value );
239                                                        if ( value.getClass().isArray() )
240                                                        {
241                                                            evaluateArray( value );
242                                                        }
243                                                        else
244                                                        {
245                                                            interpolationTargets.add( value );
246                                                        }
247                                                    }
248                                                }
249                                                else
250                                                {
251                                                    // add the null back in...not sure what else to do...
252                                                    c.add( value );
253                                                }
254                                            }
255                                        }
256                                    }
257                                    else if ( Map.class.isAssignableFrom( type ) )
258                                    {
259                                        Map<Object, Object> m = (Map<Object, Object>) fields[i].get( target );
260                                        if ( m != null && !m.isEmpty() )
261                                        {
262                                            for ( Map.Entry<Object, Object> entry : m.entrySet() )
263                                            {
264                                                Object value = entry.getValue();
265    
266                                                if ( value != null )
267                                                {
268                                                    if ( String.class == value.getClass() )
269                                                    {
270                                                        String interpolated =
271                                                            modelInterpolator.interpolateInternal( (String) value,
272                                                                                                   valueSources,
273                                                                                                   postProcessors,
274                                                                                                   debugEnabled );
275    
276                                                        if ( !interpolated.equals( value ) )
277                                                        {
278                                                            try
279                                                            {
280                                                                entry.setValue( interpolated );
281                                                            }
282                                                            catch ( UnsupportedOperationException e )
283                                                            {
284                                                                if ( debugEnabled && logger != null )
285                                                                {
286                                                                    logger.debug( "Skipping interpolation of field: "
287                                                                        + fields[i] + " (key: " + entry.getKey() + ") in: "
288                                                                        + cls.getName()
289                                                                        + "; it is an unmodifiable collection." );
290                                                                }
291                                                                continue;
292                                                            }
293                                                        }
294                                                    }
295                                                    else
296                                                    {
297                                                        if ( value.getClass().isArray() )
298                                                        {
299                                                            evaluateArray( value );
300                                                        }
301                                                        else
302                                                        {
303                                                            interpolationTargets.add( value );
304                                                        }
305                                                    }
306                                                }
307                                            }
308                                        }
309                                    }
310                                    else
311                                    {
312                                        Object value = fields[i].get( target );
313                                        if ( value != null )
314                                        {
315                                            if ( fields[i].getType().isArray() )
316                                            {
317                                                evaluateArray( value );
318                                            }
319                                            else
320                                            {
321                                                interpolationTargets.add( value );
322                                            }
323                                        }
324                                    }
325                                }
326                                catch ( IllegalArgumentException e )
327                                {
328                                    throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i]
329                                        + " on class: " + cls.getName(), e );
330                                }
331                                catch ( IllegalAccessException e )
332                                {
333                                    throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i]
334                                        + " on class: " + cls.getName(), e );
335                                }
336                            }
337                            finally
338                            {
339                                fields[i].setAccessible( isAccessible );
340                            }
341                        }
342                    }
343    
344                    traverseObjectWithParents( cls.getSuperclass(), target );
345                }
346            }
347    
348            private boolean isQualifiedForInterpolation( Class<?> cls )
349            {
350                return !cls.getPackage().getName().startsWith( "java" );
351            }
352    
353            private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
354            {
355                if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) )
356                {
357                    fieldIsPrimitiveByClass.put( fieldType, Boolean.valueOf( fieldType.isPrimitive() ) );
358                }
359    
360                if ( fieldIsPrimitiveByClass.get( fieldType ).booleanValue() )
361                {
362                    return false;
363                }
364    
365    //            if ( fieldType.isPrimitive() )
366    //            {
367    //                return false;
368    //            }
369    
370                if ( "parent".equals( field.getName() ) )
371                {
372                    return false;
373                }
374    
375                return true;
376            }
377    
378            private void evaluateArray( Object target )
379                throws ModelInterpolationException
380            {
381                int len = Array.getLength( target );
382                for ( int i = 0; i < len; i++ )
383                {
384                    Object value = Array.get( target, i );
385                    if ( value != null )
386                    {
387                        if ( String.class == value.getClass() )
388                        {
389                            String interpolated =
390                                modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
391                                                                       debugEnabled );
392    
393                            if ( !interpolated.equals( value ) )
394                            {
395                                Array.set( target, i, interpolated );
396                            }
397                        }
398                        else
399                        {
400                            interpolationTargets.add( value );
401                        }
402                    }
403                }
404            }
405        }
406    
407    }