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 for (String prefix : getProjectPrefixes(request)) {
184 if (expression.startsWith(prefix)) {
185 String subExpr = expression.substring(prefix.length());
186 String v = projectProperty(model, projectDir, subExpr, true);
187 if (v != null) {
188 return v;
189 }
190 }
191 }
192
193 String value = request.getUserProperties().get(expression);
194
195 if (value == null) {
196 value = model.getProperties().get(expression);
197 }
198
199 if (value == null) {
200 value = request.getSystemProperties().get(expression);
201 }
202
203 if (value == null) {
204 value = request.getSystemProperties().get("env." + expression);
205 }
206
207 if (value == null) {
208 value = projectProperty(model, projectDir, expression, false);
209 }
210 return value;
211 }
212
213 String projectProperty(Model model, Path projectDir, String subExpr, boolean prefixed) {
214 if (projectDir != null) {
215 if (subExpr.equals("basedir")) {
216 return projectDir.toAbsolutePath().toString();
217 } else if (subExpr.startsWith("basedir.")) {
218 try {
219 Object value = ReflectionValueExtractor.evaluate(subExpr, projectDir.toAbsolutePath(), true);
220 if (value != null) {
221 return value.toString();
222 }
223 } catch (Exception e) {
224
225 }
226 } else if (prefixed && subExpr.equals("baseUri")) {
227 return projectDir.toAbsolutePath().toUri().toASCIIString();
228 } else if (prefixed && subExpr.startsWith("baseUri.")) {
229 try {
230 Object value = ReflectionValueExtractor.evaluate(
231 subExpr, projectDir.toAbsolutePath().toUri(), true);
232 if (value != null) {
233 return value.toString();
234 }
235 } catch (Exception e) {
236
237 }
238 } else if (prefixed && subExpr.equals("rootDirectory")) {
239 return rootLocator.findMandatoryRoot(projectDir).toString();
240 } else if (prefixed && subExpr.startsWith("rootDirectory.")) {
241 try {
242 Object value =
243 ReflectionValueExtractor.evaluate(subExpr, rootLocator.findMandatoryRoot(projectDir), true);
244 if (value != null) {
245 return value.toString();
246 }
247 } catch (Exception e) {
248
249 }
250 }
251 }
252 try {
253 Object value = ReflectionValueExtractor.evaluate(subExpr, model, false);
254 if (value != null) {
255 return value.toString();
256 }
257 } catch (Exception e) {
258
259 }
260 return null;
261 }
262 }