1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project.interpolation;
20
21 import java.io.File;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Field;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.WeakHashMap;
32
33 import org.apache.maven.model.Model;
34 import org.apache.maven.project.ProjectBuilderConfiguration;
35 import org.apache.maven.project.path.PathTranslator;
36 import org.codehaus.plexus.component.annotations.Component;
37 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
38 import org.codehaus.plexus.interpolation.Interpolator;
39 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
40 import org.codehaus.plexus.interpolation.ValueSource;
41 import org.codehaus.plexus.logging.Logger;
42
43
44
45
46 @Deprecated
47 @Component(role = ModelInterpolator.class)
48 public class StringSearchModelInterpolator extends AbstractStringBasedModelInterpolator {
49
50 private static final Map<Class<?>, Field[]> FIELDS_BY_CLASS = new WeakHashMap<>();
51 private static final Map<Class<?>, Boolean> PRIMITIVE_BY_CLASS = new WeakHashMap<>();
52
53 public StringSearchModelInterpolator() {}
54
55 public StringSearchModelInterpolator(PathTranslator pathTranslator) {
56 super(pathTranslator);
57 }
58
59 public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled)
60 throws ModelInterpolationException {
61 interpolateObject(model, model, projectDir, config, debugEnabled);
62
63 return model;
64 }
65
66 protected void interpolateObject(
67 Object obj, Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled)
68 throws ModelInterpolationException {
69 try {
70 List<ValueSource> valueSources = createValueSources(model, projectDir, config);
71 List<InterpolationPostProcessor> postProcessors = createPostProcessors(model, projectDir, config);
72
73 InterpolateObjectAction action =
74 new InterpolateObjectAction(obj, valueSources, postProcessors, debugEnabled, this, getLogger());
75
76 ModelInterpolationException error = AccessController.doPrivileged(action);
77
78 if (error != null) {
79 throw error;
80 }
81 } finally {
82 getInterpolator().clearAnswers();
83 }
84 }
85
86 protected Interpolator createInterpolator() {
87 StringSearchInterpolator interpolator = new StringSearchInterpolator();
88 interpolator.setCacheAnswers(true);
89
90 return interpolator;
91 }
92
93 private static final class InterpolateObjectAction implements PrivilegedAction<ModelInterpolationException> {
94
95 private final boolean debugEnabled;
96 private final LinkedList<Object> interpolationTargets;
97 private final StringSearchModelInterpolator modelInterpolator;
98 private final Logger logger;
99 private final List<ValueSource> valueSources;
100 private final List<InterpolationPostProcessor> postProcessors;
101
102 InterpolateObjectAction(
103 Object target,
104 List<ValueSource> valueSources,
105 List<InterpolationPostProcessor> postProcessors,
106 boolean debugEnabled,
107 StringSearchModelInterpolator modelInterpolator,
108 Logger logger) {
109 this.valueSources = valueSources;
110 this.postProcessors = postProcessors;
111 this.debugEnabled = debugEnabled;
112
113 this.interpolationTargets = new LinkedList<>();
114 interpolationTargets.add(target);
115
116 this.modelInterpolator = modelInterpolator;
117 this.logger = logger;
118 }
119
120 public ModelInterpolationException run() {
121 while (!interpolationTargets.isEmpty()) {
122 Object obj = interpolationTargets.removeFirst();
123
124 try {
125 traverseObjectWithParents(obj.getClass(), obj);
126 } catch (ModelInterpolationException e) {
127 return e;
128 }
129 }
130
131 return null;
132 }
133
134 @SuppressWarnings({"unchecked", "checkstyle:methodlength"})
135 private void traverseObjectWithParents(Class<?> cls, Object target) throws ModelInterpolationException {
136 if (cls == null) {
137 return;
138 }
139
140 if (cls.isArray()) {
141 evaluateArray(target);
142 } else if (isQualifiedForInterpolation(cls)) {
143 Field[] fields = FIELDS_BY_CLASS.get(cls);
144 if (fields == null) {
145 fields = cls.getDeclaredFields();
146 FIELDS_BY_CLASS.put(cls, fields);
147 }
148
149 for (Field field : fields) {
150 Class<?> type = field.getType();
151 if (isQualifiedForInterpolation(field, type)) {
152 boolean isAccessible = field.isAccessible();
153 field.setAccessible(true);
154 try {
155 try {
156 if (String.class == type) {
157 String value = (String) field.get(target);
158 if (value != null) {
159 String interpolated = modelInterpolator.interpolateInternal(
160 value, valueSources, postProcessors, debugEnabled);
161
162 if (!interpolated.equals(value)) {
163 field.set(target, interpolated);
164 }
165 }
166 } else if (Collection.class.isAssignableFrom(type)) {
167 Collection<Object> c = (Collection<Object>) field.get(target);
168 if (c != null && !c.isEmpty()) {
169 List<Object> originalValues = new ArrayList<>(c);
170 try {
171 c.clear();
172 } catch (UnsupportedOperationException e) {
173 if (debugEnabled && logger != null) {
174 logger.debug("Skipping interpolation of field: " + field + " in: "
175 + cls.getName()
176 + "; it is an unmodifiable collection.");
177 }
178 continue;
179 }
180
181 for (Object value : originalValues) {
182 if (value != null) {
183 if (String.class == value.getClass()) {
184 String interpolated = modelInterpolator.interpolateInternal(
185 (String) value, valueSources, postProcessors, debugEnabled);
186
187 if (!interpolated.equals(value)) {
188 c.add(interpolated);
189 } else {
190 c.add(value);
191 }
192 } else {
193 c.add(value);
194 if (value.getClass().isArray()) {
195 evaluateArray(value);
196 } else {
197 interpolationTargets.add(value);
198 }
199 }
200 } else {
201
202 c.add(value);
203 }
204 }
205 }
206 } else if (Map.class.isAssignableFrom(type)) {
207 Map<Object, Object> m = (Map<Object, Object>) field.get(target);
208 if (m != null && !m.isEmpty()) {
209 for (Map.Entry<Object, Object> entry : m.entrySet()) {
210 Object value = entry.getValue();
211
212 if (value != null) {
213 if (String.class == value.getClass()) {
214 String interpolated = modelInterpolator.interpolateInternal(
215 (String) value, valueSources, postProcessors, debugEnabled);
216
217 if (!interpolated.equals(value)) {
218 try {
219 entry.setValue(interpolated);
220 } catch (UnsupportedOperationException e) {
221 if (debugEnabled && logger != null) {
222 logger.debug("Skipping interpolation of field: " + field
223 + " (key: " + entry.getKey() + ") in: "
224 + cls.getName()
225 + "; it is an unmodifiable collection.");
226 }
227 }
228 }
229 } else {
230 if (value.getClass().isArray()) {
231 evaluateArray(value);
232 } else {
233 interpolationTargets.add(value);
234 }
235 }
236 }
237 }
238 }
239 } else {
240 Object value = field.get(target);
241 if (value != null) {
242 if (field.getType().isArray()) {
243 evaluateArray(value);
244 } else {
245 interpolationTargets.add(value);
246 }
247 }
248 }
249 } catch (IllegalArgumentException | IllegalAccessException e) {
250 throw new ModelInterpolationException(
251 "Failed to interpolate field: " + field + " on class: " + cls.getName(), e);
252 }
253 } finally {
254 field.setAccessible(isAccessible);
255 }
256 }
257 }
258
259 traverseObjectWithParents(cls.getSuperclass(), target);
260 }
261 }
262
263 private boolean isQualifiedForInterpolation(Class<?> cls) {
264 return !cls.getPackage().getName().startsWith("java");
265 }
266
267 private boolean isQualifiedForInterpolation(Field field, Class<?> fieldType) {
268 if (!PRIMITIVE_BY_CLASS.containsKey(fieldType)) {
269 PRIMITIVE_BY_CLASS.put(fieldType, fieldType.isPrimitive());
270 }
271
272 if (PRIMITIVE_BY_CLASS.get(fieldType)) {
273 return false;
274 }
275
276
277
278
279
280
281 if ("parent".equals(field.getName())) {
282 return false;
283 }
284
285 return true;
286 }
287
288 private void evaluateArray(Object target) throws ModelInterpolationException {
289 int len = Array.getLength(target);
290 for (int i = 0; i < len; i++) {
291 Object value = Array.get(target, i);
292 if (value != null) {
293 if (String.class == value.getClass()) {
294 String interpolated = modelInterpolator.interpolateInternal(
295 (String) value, valueSources, postProcessors, debugEnabled);
296
297 if (!interpolated.equals(value)) {
298 Array.set(target, i, interpolated);
299 }
300 } else {
301 interpolationTargets.add(value);
302 }
303 }
304 }
305 }
306 }
307 }