001 package org.apache.maven.model.interpolation;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.model.Model;
023 import org.apache.maven.model.building.ModelBuildingRequest;
024 import org.apache.maven.model.building.ModelProblemCollector;
025 import org.apache.maven.model.building.ModelProblem.Severity;
026 import org.apache.maven.model.path.PathTranslator;
027 import org.apache.maven.model.path.UrlNormalizer;
028 import org.codehaus.plexus.component.annotations.Requirement;
029 import org.codehaus.plexus.interpolation.AbstractValueSource;
030 import org.codehaus.plexus.interpolation.InterpolationException;
031 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
032 import org.codehaus.plexus.interpolation.Interpolator;
033 import org.codehaus.plexus.interpolation.MapBasedValueSource;
034 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
035 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
036 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
037 import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
038 import org.codehaus.plexus.interpolation.RecursionInterceptor;
039 import org.codehaus.plexus.interpolation.ValueSource;
040
041 import java.io.File;
042 import java.util.ArrayList;
043 import java.util.Arrays;
044 import java.util.Collection;
045 import java.util.HashSet;
046 import java.util.List;
047 import java.util.Properties;
048 import org.apache.maven.model.building.ModelProblem.Version;
049 import org.apache.maven.model.building.ModelProblemCollectorRequest;
050
051 /**
052 * Use a regular expression search to find and resolve expressions within the POM.
053 *
054 * @author jdcasey Created on Feb 3, 2005
055 */
056 public abstract class AbstractStringBasedModelInterpolator
057 implements ModelInterpolator
058 {
059
060 /**
061 * The default format used for build timestamps.
062 */
063 static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyyMMdd-HHmm";
064
065 /**
066 * The name of a property that if present in the model's {@code <properties>} section specifies a custom format for
067 * build timestamps. See {@link java.text.SimpleDateFormat} for details on the format.
068 */
069 private static final String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
070
071 private static final List<String> PROJECT_PREFIXES = Arrays.asList( "pom.", "project." );
072
073 private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
074
075 static
076 {
077 Collection<String> translatedPrefixes = new HashSet<String>();
078
079 // MNG-1927, MNG-2124, MNG-3355:
080 // If the build section is present and the project directory is non-null, we should make
081 // sure interpolation of the directories below uses translated paths.
082 // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
083 // code below...
084 translatedPrefixes.add( "build.directory" );
085 translatedPrefixes.add( "build.outputDirectory" );
086 translatedPrefixes.add( "build.testOutputDirectory" );
087 translatedPrefixes.add( "build.sourceDirectory" );
088 translatedPrefixes.add( "build.testSourceDirectory" );
089 translatedPrefixes.add( "build.scriptSourceDirectory" );
090 translatedPrefixes.add( "reporting.outputDirectory" );
091
092 TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
093 }
094
095 @Requirement
096 private PathTranslator pathTranslator;
097
098 @Requirement
099 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 }