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