View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.model.interpolation;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.net.URI;
25  import java.nio.file.Path;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.maven.api.model.Model;
35  import org.apache.maven.model.building.ModelBuildingRequest;
36  import org.apache.maven.model.building.ModelProblemCollector;
37  import org.apache.maven.model.path.PathTranslator;
38  import org.apache.maven.model.path.UrlNormalizer;
39  import org.apache.maven.model.root.RootLocator;
40  import org.codehaus.plexus.interpolation.AbstractValueSource;
41  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
42  import org.codehaus.plexus.interpolation.MapBasedValueSource;
43  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
44  import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
45  import org.codehaus.plexus.interpolation.RecursionInterceptor;
46  import org.codehaus.plexus.interpolation.ValueSource;
47  
48  /**
49   * Use a regular expression search to find and resolve expressions within the POM.
50   *
51   */
52  public abstract class AbstractStringBasedModelInterpolator implements ModelInterpolator {
53      private static final String PREFIX_PROJECT = "project.";
54      private static final String PREFIX_POM = "pom.";
55      private static final List<String> PROJECT_PREFIXES_3_1 = Arrays.asList(PREFIX_POM, PREFIX_PROJECT);
56      private static final List<String> PROJECT_PREFIXES_4_0 = Collections.singletonList(PREFIX_PROJECT);
57  
58      private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
59  
60      static {
61          Collection<String> translatedPrefixes = new HashSet<>();
62  
63          // MNG-1927, MNG-2124, MNG-3355:
64          // If the build section is present and the project directory is non-null, we should make
65          // sure interpolation of the directories below uses translated paths.
66          // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
67          // code below...
68          translatedPrefixes.add("build.directory");
69          translatedPrefixes.add("build.outputDirectory");
70          translatedPrefixes.add("build.testOutputDirectory");
71          translatedPrefixes.add("build.sourceDirectory");
72          translatedPrefixes.add("build.testSourceDirectory");
73          translatedPrefixes.add("build.scriptSourceDirectory");
74          translatedPrefixes.add("reporting.outputDirectory");
75  
76          TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
77      }
78  
79      private final PathTranslator pathTranslator;
80      private final UrlNormalizer urlNormalizer;
81  
82      private final RootLocator rootLocator;
83  
84      @Inject
85      public AbstractStringBasedModelInterpolator(
86              PathTranslator pathTranslator, UrlNormalizer urlNormalizer, RootLocator rootLocator) {
87          this.pathTranslator = pathTranslator;
88          this.urlNormalizer = urlNormalizer;
89          this.rootLocator = rootLocator;
90      }
91  
92      @Override
93      public org.apache.maven.model.Model interpolateModel(
94              org.apache.maven.model.Model model,
95              File projectDir,
96              ModelBuildingRequest request,
97              ModelProblemCollector problems) {
98          return new org.apache.maven.model.Model(interpolateModel(model.getDelegate(), projectDir, request, problems));
99      }
100 
101     protected List<String> getProjectPrefixes(ModelBuildingRequest config) {
102         return config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0
103                 ? PROJECT_PREFIXES_4_0
104                 : PROJECT_PREFIXES_3_1;
105     }
106 
107     protected List<ValueSource> createValueSources(
108             final Model model,
109             final File projectDir,
110             final ModelBuildingRequest config,
111             ModelProblemCollector problems) {
112         Map<String, String> modelProperties = model.getProperties();
113 
114         ValueSource projectPrefixValueSource;
115         ValueSource prefixlessObjectBasedValueSource;
116         if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0) {
117             projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_4_0, model, false);
118             prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
119         } else {
120             projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_3_1, model, false);
121             if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
122                 projectPrefixValueSource =
123                         new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems);
124             }
125 
126             prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
127             if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
128                 prefixlessObjectBasedValueSource =
129                         new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems);
130             }
131         }
132 
133         // NOTE: Order counts here!
134         List<ValueSource> valueSources = new ArrayList<>(9);
135 
136         if (projectDir != null) {
137             ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
138                     new AbstractValueSource(false) {
139                         @Override
140                         public Object getValue(String expression) {
141                             if ("basedir".equals(expression)) {
142                                 return projectDir.getAbsoluteFile().toPath().toString();
143                             } else if (expression.startsWith("basedir.")) {
144                                 Path basedir = projectDir.getAbsoluteFile().toPath();
145                                 return new ObjectBasedValueSource(basedir)
146                                         .getValue(expression.substring("basedir.".length()));
147                             }
148                             return null;
149                         }
150                     },
151                     getProjectPrefixes(config),
152                     true);
153             valueSources.add(basedirValueSource);
154 
155             ValueSource baseUriValueSource = new PrefixedValueSourceWrapper(
156                     new AbstractValueSource(false) {
157                         @Override
158                         public Object getValue(String expression) {
159                             if ("baseUri".equals(expression)) {
160                                 return projectDir
161                                         .getAbsoluteFile()
162                                         .toPath()
163                                         .toUri()
164                                         .toASCIIString();
165                             } else if (expression.startsWith("baseUri.")) {
166                                 URI baseUri =
167                                         projectDir.getAbsoluteFile().toPath().toUri();
168                                 return new ObjectBasedValueSource(baseUri)
169                                         .getValue(expression.substring("baseUri.".length()));
170                             }
171                             return null;
172                         }
173                     },
174                     getProjectPrefixes(config),
175                     false);
176             valueSources.add(baseUriValueSource);
177             valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), modelProperties));
178         }
179 
180         valueSources.add(new PrefixedValueSourceWrapper(
181                 new AbstractValueSource(false) {
182                     @Override
183                     public Object getValue(String expression) {
184                         if ("rootDirectory".equals(expression)) {
185                             Path base = projectDir != null ? projectDir.toPath() : null;
186                             Path root = rootLocator.findMandatoryRoot(base);
187                             return root.toFile().getPath();
188                         } else if (expression.startsWith("rootDirectory.")) {
189                             Path base = projectDir != null ? projectDir.toPath() : null;
190                             Path root = rootLocator.findMandatoryRoot(base);
191                             return new ObjectBasedValueSource(root)
192                                     .getValue(expression.substring("rootDirectory.".length()));
193                         }
194                         return null;
195                     }
196                 },
197                 getProjectPrefixes(config)));
198 
199         valueSources.add(projectPrefixValueSource);
200 
201         valueSources.add(new MapBasedValueSource(config.getUserProperties()));
202 
203         valueSources.add(new MapBasedValueSource(modelProperties));
204 
205         valueSources.add(new MapBasedValueSource(config.getSystemProperties()));
206 
207         valueSources.add(new AbstractValueSource(false) {
208             @Override
209             public Object getValue(String expression) {
210                 return config.getSystemProperties().getProperty("env." + expression);
211             }
212         });
213 
214         valueSources.add(prefixlessObjectBasedValueSource);
215 
216         return valueSources;
217     }
218 
219     protected List<? extends InterpolationPostProcessor> createPostProcessors(
220             final Model model, final File projectDir, final ModelBuildingRequest config) {
221         List<InterpolationPostProcessor> processors = new ArrayList<>(2);
222         if (projectDir != null) {
223             processors.add(new PathTranslatingPostProcessor(
224                     getProjectPrefixes(config), TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator));
225         }
226         processors.add(new UrlNormalizingPostProcessor(urlNormalizer));
227         return processors;
228     }
229 
230     protected RecursionInterceptor createRecursionInterceptor(ModelBuildingRequest config) {
231         return new PrefixAwareRecursionInterceptor(getProjectPrefixes(config));
232     }
233 }