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