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