1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.impl.model;
20
21 import java.nio.file.Path;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.function.BinaryOperator;
30 import java.util.function.Function;
31 import java.util.function.UnaryOperator;
32
33 import org.apache.maven.api.di.Inject;
34 import org.apache.maven.api.di.Named;
35 import org.apache.maven.api.di.Singleton;
36 import org.apache.maven.api.model.Model;
37 import org.apache.maven.api.services.BuilderProblem;
38 import org.apache.maven.api.services.Interpolator;
39 import org.apache.maven.api.services.InterpolatorException;
40 import org.apache.maven.api.services.ModelBuilderRequest;
41 import org.apache.maven.api.services.ModelProblem;
42 import org.apache.maven.api.services.ModelProblemCollector;
43 import org.apache.maven.api.services.model.ModelInterpolator;
44 import org.apache.maven.api.services.model.PathTranslator;
45 import org.apache.maven.api.services.model.RootLocator;
46 import org.apache.maven.api.services.model.UrlNormalizer;
47 import org.apache.maven.impl.model.reflection.ReflectionValueExtractor;
48 import org.apache.maven.model.v4.MavenTransformer;
49
50 @Named
51 @Singleton
52 public class DefaultModelInterpolator implements ModelInterpolator {
53
54 private static final String PREFIX_PROJECT = "project.";
55 private static final String PREFIX_POM = "pom.";
56 private static final List<String> PROJECT_PREFIXES_3_1 = Arrays.asList(PREFIX_POM, PREFIX_PROJECT);
57 private static final List<String> PROJECT_PREFIXES_4_0 = Collections.singletonList(PREFIX_PROJECT);
58
59
60
61
62
63
64 private static final Set<String> TRANSLATED_PATH_EXPRESSIONS = Set.of(
65 "build.directory",
66 "build.outputDirectory",
67 "build.testOutputDirectory",
68 "build.sourceDirectory",
69 "build.testSourceDirectory",
70 "build.scriptSourceDirectory",
71 "reporting.outputDirectory");
72
73 private static final Set<String> URL_EXPRESSIONS = Set.of(
74 "project.url",
75 "project.scm.url",
76 "project.scm.connection",
77 "project.scm.developerConnection",
78 "project.distributionManagement.site.url");
79
80 private final PathTranslator pathTranslator;
81 private final UrlNormalizer urlNormalizer;
82 private final RootLocator rootLocator;
83 private final Interpolator interpolator;
84
85 @Inject
86 public DefaultModelInterpolator(
87 PathTranslator pathTranslator,
88 UrlNormalizer urlNormalizer,
89 RootLocator rootLocator,
90 Interpolator interpolator) {
91 this.pathTranslator = pathTranslator;
92 this.urlNormalizer = urlNormalizer;
93 this.rootLocator = rootLocator;
94 this.interpolator = interpolator;
95 }
96
97 interface InnerInterpolator {
98 String interpolate(String value);
99 }
100
101 @Override
102 public Model interpolateModel(
103 Model model, Path projectDir, ModelBuilderRequest request, ModelProblemCollector problems) {
104 InnerInterpolator innerInterpolator = createInterpolator(model, projectDir, request, problems);
105 return new MavenTransformer(innerInterpolator::interpolate).visit(model);
106 }
107
108 private InnerInterpolator createInterpolator(
109 Model model, Path projectDir, ModelBuilderRequest request, ModelProblemCollector problems) {
110
111 Map<String, Optional<String>> cache = new HashMap<>();
112 Function<String, Optional<String>> ucb =
113 v -> Optional.ofNullable(callback(model, projectDir, request, problems, v));
114 UnaryOperator<String> cb = v -> cache.computeIfAbsent(v, ucb).orElse(null);
115 BinaryOperator<String> postprocessor = (e, v) -> postProcess(projectDir, request, e, v);
116 return value -> {
117 try {
118 return interpolator.interpolate(value, cb, postprocessor, false);
119 } catch (InterpolatorException e) {
120 problems.add(BuilderProblem.Severity.ERROR, ModelProblem.Version.BASE, e.getMessage(), e);
121 return null;
122 }
123 };
124 }
125
126 protected List<String> getProjectPrefixes(ModelBuilderRequest request) {
127 return request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT
128 ? PROJECT_PREFIXES_4_0
129 : PROJECT_PREFIXES_3_1;
130 }
131
132 String callback(
133 Model model,
134 Path projectDir,
135 ModelBuilderRequest request,
136 ModelProblemCollector problems,
137 String expression) {
138 String value = doCallback(model, projectDir, request, problems, expression);
139 if (value != null) {
140
141 }
142 return value;
143 }
144
145 private String postProcess(Path projectDir, ModelBuilderRequest request, String expression, String value) {
146
147 String exp = unprefix(expression, getProjectPrefixes(request));
148 if (TRANSLATED_PATH_EXPRESSIONS.contains(exp)) {
149 value = pathTranslator.alignToBaseDirectory(value, projectDir);
150 }
151
152 if (URL_EXPRESSIONS.contains(expression)) {
153 value = urlNormalizer.normalize(value);
154 }
155 return value;
156 }
157
158 private String unprefix(String expression, List<String> prefixes) {
159 for (String prefix : prefixes) {
160 if (expression.startsWith(prefix)) {
161 return expression.substring(prefix.length());
162 }
163 }
164 return expression;
165 }
166
167 String doCallback(
168 Model model,
169 Path projectDir,
170 ModelBuilderRequest request,
171 ModelProblemCollector problems,
172 String expression) {
173
174 if ("basedir".equals(expression)) {
175 return projectProperty(model, projectDir, expression, false);
176 }
177
178 if ("build.timestamp".equals(expression) || "maven.build.timestamp".equals(expression)) {
179 return new MavenBuildTimestamp(request.getSession().getStartTime(), model.getProperties())
180 .formattedTimestamp();
181 }
182
183 String value = request.getUserProperties().get(expression);
184
185 if (value == null) {
186 value = model.getProperties().get(expression);
187 }
188
189 if (value == null) {
190 for (String prefix : getProjectPrefixes(request)) {
191 if (expression.startsWith(prefix)) {
192 String subExpr = expression.substring(prefix.length());
193 value = projectProperty(model, projectDir, subExpr, true);
194 if (value != null) {
195 return value;
196 }
197 }
198 }
199 }
200
201 if (value == null) {
202 value = request.getSystemProperties().get(expression);
203 }
204
205 if (value == null) {
206 value = request.getSystemProperties().get("env." + expression);
207 }
208
209 if (value == null) {
210 value = projectProperty(model, projectDir, expression, false);
211 }
212 return value;
213 }
214
215 String projectProperty(Model model, Path projectDir, String subExpr, boolean prefixed) {
216 if (projectDir != null) {
217 if (subExpr.equals("basedir")) {
218 return projectDir.toAbsolutePath().toString();
219 } else if (subExpr.startsWith("basedir.")) {
220 try {
221 Object value = ReflectionValueExtractor.evaluate(subExpr, projectDir.toAbsolutePath(), true);
222 if (value != null) {
223 return value.toString();
224 }
225 } catch (Exception e) {
226
227 }
228 } else if (prefixed && subExpr.equals("baseUri")) {
229 return projectDir.toAbsolutePath().toUri().toASCIIString();
230 } else if (prefixed && subExpr.startsWith("baseUri.")) {
231 try {
232 Object value = ReflectionValueExtractor.evaluate(
233 subExpr, projectDir.toAbsolutePath().toUri(), true);
234 if (value != null) {
235 return value.toString();
236 }
237 } catch (Exception e) {
238
239 }
240 } else if (prefixed && subExpr.equals("rootDirectory")) {
241 return rootLocator.findMandatoryRoot(projectDir).toString();
242 } else if (prefixed && subExpr.startsWith("rootDirectory.")) {
243 try {
244 Object value =
245 ReflectionValueExtractor.evaluate(subExpr, rootLocator.findMandatoryRoot(projectDir), true);
246 if (value != null) {
247 return value.toString();
248 }
249 } catch (Exception e) {
250
251 }
252 }
253 }
254 try {
255 Object value = ReflectionValueExtractor.evaluate(subExpr, model, false);
256 if (value != null) {
257 return value.toString();
258 }
259 } catch (Exception e) {
260
261 }
262 return null;
263 }
264 }