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