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.ModelProblemCollector;
25  import org.apache.maven.model.building.ModelProblem.Severity;
26  import org.apache.maven.model.path.PathTranslator;
27  import org.apache.maven.model.path.UrlNormalizer;
28  import org.codehaus.plexus.component.annotations.Requirement;
29  import org.codehaus.plexus.interpolation.AbstractValueSource;
30  import org.codehaus.plexus.interpolation.InterpolationException;
31  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
32  import org.codehaus.plexus.interpolation.Interpolator;
33  import org.codehaus.plexus.interpolation.MapBasedValueSource;
34  import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
35  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
36  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
37  import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
38  import org.codehaus.plexus.interpolation.RecursionInterceptor;
39  import org.codehaus.plexus.interpolation.ValueSource;
40  
41  import java.io.File;
42  import java.util.ArrayList;
43  import java.util.Arrays;
44  import java.util.Collection;
45  import java.util.HashSet;
46  import java.util.List;
47  import java.util.Properties;
48  
49  /**
50   * Use a regular expression search to find and resolve expressions within the POM.
51   *
52   * @author jdcasey Created on Feb 3, 2005
53   * @version $Id: AbstractStringBasedModelInterpolator.java 949708 2010-05-31 10:24:20Z bentmann $
54   */
55  public abstract class AbstractStringBasedModelInterpolator
56      implements ModelInterpolator
57  {
58  
59      /**
60       * The default format used for build timestamps.
61       */
62      static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyyMMdd-HHmm";
63  
64      /**
65       * The name of a property that if present in the model's {@code <properties>} section specifies a custom format for
66       * build timestamps. See {@link java.text.SimpleDateFormat} for details on the format.
67       */
68      private static final String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
69  
70      private static final List<String> PROJECT_PREFIXES = Arrays.asList( new String[]{ "pom.", "project." } );
71  
72      private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
73  
74      static
75      {
76          Collection<String> translatedPrefixes = new HashSet<String>();
77  
78          // MNG-1927, MNG-2124, MNG-3355:
79          // If the build section is present and the project directory is non-null, we should make
80          // sure interpolation of the directories below uses translated paths.
81          // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
82          // code below...
83          translatedPrefixes.add( "build.directory" );
84          translatedPrefixes.add( "build.outputDirectory" );
85          translatedPrefixes.add( "build.testOutputDirectory" );
86          translatedPrefixes.add( "build.sourceDirectory" );
87          translatedPrefixes.add( "build.testSourceDirectory" );
88          translatedPrefixes.add( "build.scriptSourceDirectory" );
89          translatedPrefixes.add( "reporting.outputDirectory" );
90  
91          TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
92      }
93  
94      @Requirement
95      private PathTranslator pathTranslator;
96  
97      @Requirement
98      private UrlNormalizer urlNormalizer;
99  
100     private Interpolator interpolator;
101 
102     private RecursionInterceptor recursionInterceptor;
103 
104     public AbstractStringBasedModelInterpolator()
105     {
106         interpolator = createInterpolator();
107         recursionInterceptor = new PrefixAwareRecursionInterceptor( PROJECT_PREFIXES );
108     }
109 
110     public AbstractStringBasedModelInterpolator setPathTranslator( PathTranslator pathTranslator )
111     {
112         this.pathTranslator = pathTranslator;
113         return this;
114     }
115 
116     public AbstractStringBasedModelInterpolator setUrlNormalizer( UrlNormalizer urlNormalizer )
117     {
118         this.urlNormalizer = urlNormalizer;
119         return this;
120     }
121 
122     protected List<ValueSource> createValueSources( final Model model, final File projectDir,
123                                                     final ModelBuildingRequest config,
124                                                     final ModelProblemCollector problems )
125     {
126         Properties modelProperties = model.getProperties();
127 
128         ValueSource modelValueSource1 = new PrefixedObjectValueSource( PROJECT_PREFIXES, model, false );
129         if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
130         {
131             modelValueSource1 = new ProblemDetectingValueSource( modelValueSource1, "pom.", "project.", problems );
132         }
133 
134         ValueSource modelValueSource2 = new ObjectBasedValueSource( model );
135         if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
136         {
137             modelValueSource2 = new ProblemDetectingValueSource( modelValueSource2, "", "project.", problems );
138         }
139 
140         // NOTE: Order counts here!
141         List<ValueSource> valueSources = new ArrayList<ValueSource>( 9 );
142 
143         if ( projectDir != null )
144         {
145             ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false )
146             {
147                 public Object getValue( String expression )
148                 {
149                     if ( "basedir".equals( expression ) )
150                     {
151                         return projectDir.getAbsolutePath();
152                     }
153                     return null;
154                 }
155             }, PROJECT_PREFIXES, true );
156             valueSources.add( basedirValueSource );
157 
158             ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false )
159             {
160                 public Object getValue( String expression )
161                 {
162                     if ( "baseUri".equals( expression ) )
163                     {
164                         return projectDir.getAbsoluteFile().toURI().toString();
165                     }
166                     return null;
167                 }
168             }, PROJECT_PREFIXES, false );
169             valueSources.add( baseUriValueSource );
170 
171             String timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT;
172             if ( modelProperties != null )
173             {
174                 timestampFormat = modelProperties.getProperty( BUILD_TIMESTAMP_FORMAT_PROPERTY, timestampFormat );
175             }
176             valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), timestampFormat ) );
177         }
178 
179         valueSources.add( modelValueSource1 );
180 
181         valueSources.add( new MapBasedValueSource( config.getUserProperties() ) );
182 
183         valueSources.add( new MapBasedValueSource( modelProperties ) );
184 
185         valueSources.add( new MapBasedValueSource( config.getSystemProperties() ) );
186 
187         valueSources.add( new AbstractValueSource( false )
188         {
189             public Object getValue( String expression )
190             {
191                 return config.getSystemProperties().getProperty( "env." + expression );
192             }
193         } );
194 
195         valueSources.add( modelValueSource2 );
196 
197         return valueSources;
198     }
199 
200     protected List<? extends InterpolationPostProcessor> createPostProcessors( final Model model,
201                                                                                final File projectDir,
202                                                                                final ModelBuildingRequest config )
203     {
204         List<InterpolationPostProcessor> processors = new ArrayList<InterpolationPostProcessor>( 2 );
205         if ( projectDir != null )
206         {
207             processors.add( new PathTranslatingPostProcessor( PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS,
208                                                               projectDir, pathTranslator ) );
209         }
210         processors.add( new UrlNormalizingPostProcessor( urlNormalizer ) );
211         return processors;
212     }
213 
214     protected String interpolateInternal( String src, List<? extends ValueSource> valueSources,
215                                           List<? extends InterpolationPostProcessor> postProcessors,
216                                           ModelProblemCollector problems )
217     {
218         if ( src.indexOf( "${" ) < 0 )
219         {
220             return src;
221         }
222 
223         String result = src;
224         synchronized ( this )
225         {
226 
227             for ( ValueSource vs : valueSources )
228             {
229                 interpolator.addValueSource( vs );
230             }
231 
232             for ( InterpolationPostProcessor postProcessor : postProcessors )
233             {
234                 interpolator.addPostProcessor( postProcessor );
235             }
236 
237             try
238             {
239                 try
240                 {
241                     result = interpolator.interpolate( result, recursionInterceptor );
242                 }
243                 catch ( InterpolationException e )
244                 {
245                     problems.add( Severity.ERROR, e.getMessage(), null, e );
246                 }
247 
248                 interpolator.clearFeedback();
249             }
250             finally
251             {
252                 for ( ValueSource vs : valueSources )
253                 {
254                     interpolator.removeValuesSource( vs );
255                 }
256 
257                 for ( InterpolationPostProcessor postProcessor : postProcessors )
258                 {
259                     interpolator.removePostProcessor( postProcessor );
260                 }
261             }
262         }
263 
264         return result;
265     }
266 
267     protected RecursionInterceptor getRecursionInterceptor()
268     {
269         return recursionInterceptor;
270     }
271 
272     protected void setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
273     {
274         this.recursionInterceptor = recursionInterceptor;
275     }
276 
277     protected abstract Interpolator createInterpolator();
278 
279     protected final Interpolator getInterpolator()
280     {
281         return interpolator;
282     }
283 
284 }