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