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.net.URI;
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.maven.api.model.Model;
34  import org.apache.maven.model.building.ModelBuildingRequest;
35  import org.apache.maven.model.building.ModelProblemCollector;
36  import org.apache.maven.model.path.PathTranslator;
37  import org.apache.maven.model.path.UrlNormalizer;
38  import org.apache.maven.model.root.RootLocator;
39  import org.codehaus.plexus.interpolation.AbstractValueSource;
40  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
41  import org.codehaus.plexus.interpolation.MapBasedValueSource;
42  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
43  import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
44  import org.codehaus.plexus.interpolation.RecursionInterceptor;
45  import org.codehaus.plexus.interpolation.ValueSource;
46  
47  /**
48   * Use a regular expression search to find and resolve expressions within the POM.
49   *
50   */
51  public abstract class AbstractStringBasedModelInterpolator implements ModelInterpolator {
52      private static final String PREFIX_PROJECT = "project.";
53      private static final String PREFIX_POM = "pom.";
54      private static final List<String> PROJECT_PREFIXES_3_1 = Arrays.asList(PREFIX_POM, PREFIX_PROJECT);
55      private static final List<String> PROJECT_PREFIXES_4_0 = Collections.singletonList(PREFIX_PROJECT);
56  
57      private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS;
58  
59      static {
60          Collection<String> translatedPrefixes = new HashSet<>();
61  
62          // MNG-1927, MNG-2124, MNG-3355:
63          // If the build section is present and the project directory is non-null, we should make
64          // sure interpolation of the directories below uses translated paths.
65          // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
66          // code below...
67          translatedPrefixes.add("build.directory");
68          translatedPrefixes.add("build.outputDirectory");
69          translatedPrefixes.add("build.testOutputDirectory");
70          translatedPrefixes.add("build.sourceDirectory");
71          translatedPrefixes.add("build.testSourceDirectory");
72          translatedPrefixes.add("build.scriptSourceDirectory");
73          translatedPrefixes.add("reporting.outputDirectory");
74  
75          TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
76      }
77  
78      private final PathTranslator pathTranslator;
79      private final UrlNormalizer urlNormalizer;
80  
81      private final RootLocator rootLocator;
82  
83      @Inject
84      public AbstractStringBasedModelInterpolator(
85              PathTranslator pathTranslator, UrlNormalizer urlNormalizer, RootLocator rootLocator) {
86          this.pathTranslator = pathTranslator;
87          this.urlNormalizer = urlNormalizer;
88          this.rootLocator = rootLocator;
89      }
90  
91      @Override
92      public org.apache.maven.model.Model interpolateModel(
93              org.apache.maven.model.Model model,
94              java.io.File projectDir,
95              ModelBuildingRequest request,
96              ModelProblemCollector problems) {
97          return new org.apache.maven.model.Model(interpolateModel(
98                  model.getDelegate(), projectDir != null ? projectDir.toPath() : null, request, problems));
99      }
100 
101     @Override
102     public org.apache.maven.model.Model interpolateModel(
103             org.apache.maven.model.Model model,
104             Path projectDir,
105             ModelBuildingRequest request,
106             ModelProblemCollector problems) {
107         return new org.apache.maven.model.Model(interpolateModel(model.getDelegate(), projectDir, request, problems));
108     }
109 
110     protected List<String> getProjectPrefixes(ModelBuildingRequest config) {
111         return config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0
112                 ? PROJECT_PREFIXES_4_0
113                 : PROJECT_PREFIXES_3_1;
114     }
115 
116     protected List<ValueSource> createValueSources(
117             final Model model,
118             final Path projectDir,
119             final ModelBuildingRequest config,
120             ModelProblemCollector problems) {
121         Map<String, String> modelProperties = model.getProperties();
122 
123         ValueSource projectPrefixValueSource;
124         ValueSource prefixlessObjectBasedValueSource;
125         if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0) {
126             projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_4_0, model, false);
127             prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
128         } else {
129             projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_3_1, model, false);
130             if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
131                 projectPrefixValueSource =
132                         new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems);
133             }
134 
135             prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model);
136             if (config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
137                 prefixlessObjectBasedValueSource =
138                         new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems);
139             }
140         }
141 
142         // NOTE: Order counts here!
143         List<ValueSource> valueSources = new ArrayList<>(9);
144 
145         if (projectDir != null) {
146             ValueSource basedirValueSource = new PrefixedValueSourceWrapper(
147                     new AbstractValueSource(false) {
148                         @Override
149                         public Object getValue(String expression) {
150                             if ("basedir".equals(expression)) {
151                                 return projectDir.toAbsolutePath().toString();
152                             } else if (expression.startsWith("basedir.")) {
153                                 Path basedir = projectDir.toAbsolutePath();
154                                 return new ObjectBasedValueSource(basedir)
155                                         .getValue(expression.substring("basedir.".length()));
156                             }
157                             return null;
158                         }
159                     },
160                     getProjectPrefixes(config),
161                     true);
162             valueSources.add(basedirValueSource);
163 
164             ValueSource baseUriValueSource = new PrefixedValueSourceWrapper(
165                     new AbstractValueSource(false) {
166                         @Override
167                         public Object getValue(String expression) {
168                             if ("baseUri".equals(expression)) {
169                                 return projectDir.toAbsolutePath().toUri().toASCIIString();
170                             } else if (expression.startsWith("baseUri.")) {
171                                 URI baseUri = projectDir.toAbsolutePath().toUri();
172                                 return new ObjectBasedValueSource(baseUri)
173                                         .getValue(expression.substring("baseUri.".length()));
174                             }
175                             return null;
176                         }
177                     },
178                     getProjectPrefixes(config),
179                     false);
180             valueSources.add(baseUriValueSource);
181             valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), modelProperties));
182         }
183 
184         valueSources.add(new PrefixedValueSourceWrapper(
185                 new AbstractValueSource(false) {
186                     @Override
187                     public Object getValue(String expression) {
188                         if ("rootDirectory".equals(expression)) {
189                             Path root = rootLocator.findMandatoryRoot(projectDir);
190                             return root.toFile().getPath();
191                         } else if (expression.startsWith("rootDirectory.")) {
192                             Path root = rootLocator.findMandatoryRoot(projectDir);
193                             return new ObjectBasedValueSource(root)
194                                     .getValue(expression.substring("rootDirectory.".length()));
195                         }
196                         return null;
197                     }
198                 },
199                 getProjectPrefixes(config)));
200 
201         valueSources.add(projectPrefixValueSource);
202 
203         valueSources.add(new MapBasedValueSource(config.getUserProperties()));
204 
205         valueSources.add(new MapBasedValueSource(modelProperties));
206 
207         valueSources.add(new MapBasedValueSource(config.getSystemProperties()));
208 
209         valueSources.add(new AbstractValueSource(false) {
210             @Override
211             public Object getValue(String expression) {
212                 return config.getSystemProperties().getProperty("env." + expression);
213             }
214         });
215 
216         valueSources.add(prefixlessObjectBasedValueSource);
217 
218         return valueSources;
219     }
220 
221     protected List<? extends InterpolationPostProcessor> createPostProcessors(
222             final Model model, final Path projectDir, final ModelBuildingRequest config) {
223         List<InterpolationPostProcessor> processors = new ArrayList<>(2);
224         if (projectDir != null) {
225             processors.add(new PathTranslatingPostProcessor(
226                     getProjectPrefixes(config), TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator));
227         }
228         processors.add(new UrlNormalizingPostProcessor(urlNormalizer));
229         return processors;
230     }
231 
232     protected RecursionInterceptor createRecursionInterceptor(ModelBuildingRequest config) {
233         return new PrefixAwareRecursionInterceptor(getProjectPrefixes(config));
234     }
235 }