1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.model.interpolation;
20
21 import java.io.File;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.concurrent.ConcurrentHashMap;
32
33 import org.apache.maven.model.InputLocation;
34 import org.apache.maven.model.Model;
35 import org.apache.maven.model.building.ModelBuildingRequest;
36 import org.apache.maven.model.building.ModelProblem.Severity;
37 import org.apache.maven.model.building.ModelProblem.Version;
38 import org.apache.maven.model.building.ModelProblemCollector;
39 import org.apache.maven.model.building.ModelProblemCollectorRequest;
40 import org.codehaus.plexus.interpolation.InterpolationException;
41 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
42 import org.codehaus.plexus.interpolation.RecursionInterceptor;
43 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
44 import org.codehaus.plexus.interpolation.ValueSource;
45
46
47
48
49
50 @Deprecated
51 public class StringSearchModelInterpolator extends AbstractStringBasedModelInterpolator {
52 private static final Map<Class<?>, InterpolateObjectAction.CacheItem> CACHED_ENTRIES =
53 new ConcurrentHashMap<>(80, 0.75f, 2);
54
55
56 private interface InnerInterpolator {
57 String interpolate(String value);
58 }
59
60 @Override
61 public Model interpolateModel(
62 Model model, File projectDir, ModelBuildingRequest config, ModelProblemCollector problems) {
63 interpolateObject(model, model, projectDir, config, problems);
64 return model;
65 }
66
67 void interpolateObject(
68 Object obj, Model model, File projectDir, ModelBuildingRequest config, ModelProblemCollector problems) {
69 List<? extends ValueSource> valueSources = createValueSources(model, projectDir, config, problems);
70 List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors(model, projectDir, config);
71
72 InnerInterpolator innerInterpolator = createInterpolator(valueSources, postProcessors, problems);
73
74 new InterpolateObjectAction(obj, innerInterpolator, problems).run();
75 }
76
77 private InnerInterpolator createInterpolator(
78 List<? extends ValueSource> valueSources,
79 List<? extends InterpolationPostProcessor> postProcessors,
80 final ModelProblemCollector problems) {
81 final Map<String, String> cache = new HashMap<>();
82 final StringSearchInterpolator interpolator = new StringSearchInterpolator();
83 interpolator.setCacheAnswers(true);
84 for (ValueSource vs : valueSources) {
85 interpolator.addValueSource(vs);
86 }
87 for (InterpolationPostProcessor postProcessor : postProcessors) {
88 interpolator.addPostProcessor(postProcessor);
89 }
90 final RecursionInterceptor recursionInterceptor = createRecursionInterceptor();
91 return new InnerInterpolator() {
92 @Override
93 public String interpolate(String value) {
94 if (value != null && value.contains("${")) {
95 String c = cache.get(value);
96 if (c == null) {
97 try {
98 c = interpolator.interpolate(value, recursionInterceptor);
99 } catch (InterpolationException e) {
100 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
101 .setMessage(e.getMessage())
102 .setException(e));
103 }
104 cache.put(value, c);
105 }
106 return c;
107 }
108 return value;
109 }
110 };
111 }
112
113 private static final class InterpolateObjectAction {
114 private final LinkedList<Object> interpolationTargets;
115
116 private final InnerInterpolator interpolator;
117
118 private final ModelProblemCollector problems;
119
120 InterpolateObjectAction(Object target, InnerInterpolator interpolator, ModelProblemCollector problems) {
121 this.interpolationTargets = new LinkedList<>();
122 interpolationTargets.add(target);
123 this.interpolator = interpolator;
124 this.problems = problems;
125 }
126
127 public Object run() {
128 while (!interpolationTargets.isEmpty()) {
129 Object obj = interpolationTargets.removeFirst();
130
131 traverseObjectWithParents(obj.getClass(), obj);
132 }
133 return null;
134 }
135
136 private String interpolate(String value) {
137 return interpolator.interpolate(value);
138 }
139
140 private void traverseObjectWithParents(Class<?> cls, Object target) {
141 if (cls == null) {
142 return;
143 }
144
145 CacheItem cacheEntry = getCacheEntry(cls);
146 if (cacheEntry.isArray()) {
147 evaluateArray(target, this);
148 } else if (cacheEntry.isQualifiedForInterpolation) {
149 cacheEntry.interpolate(target, this);
150
151 traverseObjectWithParents(cls.getSuperclass(), target);
152 }
153 }
154
155 private CacheItem getCacheEntry(Class<?> cls) {
156 CacheItem cacheItem = CACHED_ENTRIES.get(cls);
157 if (cacheItem == null) {
158 cacheItem = new CacheItem(cls);
159 CACHED_ENTRIES.put(cls, cacheItem);
160 }
161 return cacheItem;
162 }
163
164 private static void evaluateArray(Object target, InterpolateObjectAction ctx) {
165 int len = Array.getLength(target);
166 for (int i = 0; i < len; i++) {
167 Object value = Array.get(target, i);
168 if (value != null) {
169 if (String.class == value.getClass()) {
170 String interpolated = ctx.interpolate((String) value);
171
172 if (!interpolated.equals(value)) {
173 Array.set(target, i, interpolated);
174 }
175 } else {
176 ctx.interpolationTargets.add(value);
177 }
178 }
179 }
180 }
181
182 private static class CacheItem {
183 private final boolean isArray;
184
185 private final boolean isQualifiedForInterpolation;
186
187 private final CacheField[] fields;
188
189 private boolean isQualifiedForInterpolation(Class<?> cls) {
190 Package pkg = cls.getPackage();
191 if (pkg == null) {
192 return true;
193 }
194 String pkgName = pkg.getName();
195 return !pkgName.startsWith("java.") && !pkgName.startsWith("javax.");
196 }
197
198 private boolean isQualifiedForInterpolation(Field field, Class<?> fieldType) {
199 if (Map.class.equals(fieldType) && "locations".equals(field.getName())) {
200 return false;
201 }
202 if (InputLocation.class.equals(fieldType)) {
203 return false;
204 }
205
206
207 if (fieldType.isPrimitive()) {
208 return false;
209 }
210
211 return !"parent".equals(field.getName());
212 }
213
214 CacheItem(Class clazz) {
215 this.isQualifiedForInterpolation = isQualifiedForInterpolation(clazz);
216 this.isArray = clazz.isArray();
217 List<CacheField> fields = new ArrayList<>();
218 if (isQualifiedForInterpolation) {
219 for (Field currentField : clazz.getDeclaredFields()) {
220 Class<?> type = currentField.getType();
221 if (isQualifiedForInterpolation(currentField, type)) {
222 if (String.class == type) {
223 if (!Modifier.isFinal(currentField.getModifiers())) {
224 fields.add(new StringField(currentField));
225 }
226 } else if (List.class.isAssignableFrom(type)) {
227 fields.add(new ListField(currentField));
228 } else if (Collection.class.isAssignableFrom(type)) {
229 throw new RuntimeException("We dont interpolate into collections, use a list instead");
230 } else if (Map.class.isAssignableFrom(type)) {
231 fields.add(new MapField(currentField));
232 } else {
233 fields.add(new ObjectField(currentField));
234 }
235 }
236 }
237 }
238 this.fields = fields.toArray(new CacheField[0]);
239 }
240
241 void interpolate(Object target, InterpolateObjectAction interpolateObjectAction) {
242 for (CacheField field : fields) {
243 field.interpolate(target, interpolateObjectAction);
244 }
245 }
246
247 boolean isArray() {
248 return isArray;
249 }
250 }
251
252 abstract static class CacheField {
253 final Field field;
254
255 CacheField(Field field) {
256 this.field = field;
257 field.setAccessible(true);
258 }
259
260 void interpolate(Object target, InterpolateObjectAction interpolateObjectAction) {
261 try {
262 doInterpolate(target, interpolateObjectAction);
263 } catch (IllegalArgumentException e) {
264 interpolateObjectAction.problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
265 .setMessage("Failed to interpolate field3: " + field + " on class: "
266 + field.getType().getName())
267 .setException(e));
268 } catch (IllegalAccessException e) {
269 interpolateObjectAction.problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
270 .setMessage("Failed to interpolate field4: " + field + " on class: "
271 + field.getType().getName())
272 .setException(e));
273 }
274 }
275
276 abstract void doInterpolate(Object target, InterpolateObjectAction ctx) throws IllegalAccessException;
277 }
278
279 static final class StringField extends CacheField {
280 StringField(Field field) {
281 super(field);
282 }
283
284 @Override
285 void doInterpolate(Object target, InterpolateObjectAction ctx) throws IllegalAccessException {
286 String value = (String) field.get(target);
287 if (value == null) {
288 return;
289 }
290
291 String interpolated = ctx.interpolate(value);
292
293 if (interpolated != null && !interpolated.equals(value)) {
294 field.set(target, interpolated);
295 }
296 }
297 }
298
299 static final class ListField extends CacheField {
300 ListField(Field field) {
301 super(field);
302 }
303
304 @Override
305 void doInterpolate(Object target, InterpolateObjectAction ctx) throws IllegalAccessException {
306 @SuppressWarnings("unchecked")
307 List<Object> c = (List<Object>) field.get(target);
308 if (c == null) {
309 return;
310 }
311
312 for (int i = 0, size = c.size(); i < size; i++) {
313 Object value = c.get(i);
314
315 if (value != null) {
316 if (String.class == value.getClass()) {
317 String interpolated = ctx.interpolate((String) value);
318
319 if (!interpolated.equals(value)) {
320 try {
321 c.set(i, interpolated);
322 } catch (UnsupportedOperationException e) {
323 return;
324 }
325 }
326 } else {
327 if (value.getClass().isArray()) {
328 evaluateArray(value, ctx);
329 } else {
330 ctx.interpolationTargets.add(value);
331 }
332 }
333 }
334 }
335 }
336 }
337
338 static final class MapField extends CacheField {
339 MapField(Field field) {
340 super(field);
341 }
342
343 @Override
344 void doInterpolate(Object target, InterpolateObjectAction ctx) throws IllegalAccessException {
345 @SuppressWarnings("unchecked")
346 Map<Object, Object> m = (Map<Object, Object>) field.get(target);
347 if (m == null || m.isEmpty()) {
348 return;
349 }
350
351 for (Map.Entry<Object, Object> entry : m.entrySet()) {
352 Object value = entry.getValue();
353
354 if (value == null) {
355 continue;
356 }
357
358 if (String.class == value.getClass()) {
359 String interpolated = ctx.interpolate((String) value);
360
361 if (interpolated != null && !interpolated.equals(value)) {
362 try {
363 entry.setValue(interpolated);
364 } catch (UnsupportedOperationException ignore) {
365
366 }
367 }
368 } else if (value.getClass().isArray()) {
369 evaluateArray(value, ctx);
370 } else {
371 ctx.interpolationTargets.add(value);
372 }
373 }
374 }
375 }
376
377 static final class ObjectField extends CacheField {
378 private final boolean isArray;
379
380 ObjectField(Field field) {
381 super(field);
382 this.isArray = field.getType().isArray();
383 }
384
385 @Override
386 void doInterpolate(Object target, InterpolateObjectAction ctx) throws IllegalAccessException {
387 Object value = field.get(target);
388 if (value != null) {
389 if (isArray) {
390 evaluateArray(value, ctx);
391 } else {
392 ctx.interpolationTargets.add(value);
393 }
394 }
395 }
396 }
397 }
398 }