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