1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.sisu.plexus;
20
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23
24 import java.io.StringReader;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.Properties;
32
33 import com.google.inject.Injector;
34 import com.google.inject.Key;
35 import com.google.inject.Module;
36 import com.google.inject.TypeLiteral;
37 import com.google.inject.spi.TypeConverter;
38 import com.google.inject.spi.TypeConverterBinding;
39 import org.apache.maven.api.xml.XmlNode;
40 import org.apache.maven.internal.xml.XmlNodeBuilder;
41 import org.codehaus.plexus.util.xml.Xpp3Dom;
42 import org.codehaus.plexus.util.xml.pull.MXParser;
43 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
44 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
45 import org.eclipse.sisu.Priority;
46 import org.eclipse.sisu.bean.BeanProperties;
47 import org.eclipse.sisu.bean.BeanProperty;
48 import org.eclipse.sisu.inject.Logs;
49 import org.eclipse.sisu.inject.TypeArguments;
50
51
52
53
54 @Singleton
55 @Priority(10)
56 @Deprecated
57 public final class PlexusXmlBeanConverter implements PlexusBeanConverter {
58
59
60
61
62 private static final String CONVERSION_ERROR = "Cannot convert: \"%s\" to: %s";
63
64
65
66
67
68 private final Collection<TypeConverterBinding> typeConverterBindings;
69
70
71
72
73
74 @Inject
75 PlexusXmlBeanConverter(final Injector injector) {
76 typeConverterBindings = injector.getTypeConverterBindings();
77 }
78
79
80
81
82
83 @SuppressWarnings({"unchecked", "rawtypes"})
84 public Object convert(final TypeLiteral role, final String value) {
85 if (value.trim().startsWith("<")) {
86 try {
87 final MXParser parser = new MXParser();
88 parser.setInput(new StringReader(value));
89 parser.nextTag();
90
91 return parse(parser, role);
92 } catch (final Exception e) {
93 throw new IllegalArgumentException(String.format(CONVERSION_ERROR, value, role), e);
94 }
95 }
96
97 return convertText(value, role);
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111 private Object parse(final MXParser parser, final TypeLiteral<?> toType) throws Exception {
112 parser.require(XmlPullParser.START_TAG, null, null);
113
114 final Class<?> rawType = toType.getRawType();
115 if (XmlNode.class.isAssignableFrom(rawType)) {
116 return XmlNodeBuilder.build(parser);
117 }
118 if (Xpp3Dom.class.isAssignableFrom(rawType)) {
119 return new Xpp3Dom(XmlNodeBuilder.build(parser));
120 }
121 if (Properties.class.isAssignableFrom(rawType)) {
122 return parseProperties(parser);
123 }
124 if (Map.class.isAssignableFrom(rawType)) {
125 return parseMap(parser, TypeArguments.get(toType.getSupertype(Map.class), 1));
126 }
127 if (Collection.class.isAssignableFrom(rawType)) {
128 return parseCollection(parser, TypeArguments.get(toType.getSupertype(Collection.class), 0));
129 }
130 if (rawType.isArray()) {
131 return parseArray(parser, TypeArguments.get(toType, 0));
132 }
133 return parseBean(parser, toType, rawType);
134 }
135
136
137
138
139
140
141
142 private static Properties parseProperties(final XmlPullParser parser) throws Exception {
143 final Properties properties = newImplementation(parser, Properties.class);
144 while (parser.nextTag() == XmlPullParser.START_TAG) {
145 parser.nextTag();
146
147 if ("name".equals(parser.getName())) {
148 final String name = parser.nextText();
149 parser.nextTag();
150 properties.put(name, parser.nextText());
151 } else {
152 final String value = parser.nextText();
153 parser.nextTag();
154 properties.put(parser.nextText(), value);
155 }
156 parser.nextTag();
157 }
158 return properties;
159 }
160
161
162
163
164
165
166
167 private Map<String, Object> parseMap(final MXParser parser, final TypeLiteral<?> toType) throws Exception {
168 @SuppressWarnings("unchecked")
169 final Map<String, Object> map = newImplementation(parser, HashMap.class);
170 while (parser.nextTag() == XmlPullParser.START_TAG) {
171 map.put(parser.getName(), parse(parser, toType));
172 }
173 return map;
174 }
175
176
177
178
179
180
181
182 private Collection<Object> parseCollection(final MXParser parser, final TypeLiteral<?> toType) throws Exception {
183 @SuppressWarnings("unchecked")
184 final Collection<Object> collection = newImplementation(parser, ArrayList.class);
185 while (parser.nextTag() == XmlPullParser.START_TAG) {
186 collection.add(parse(parser, toType));
187 }
188 return collection;
189 }
190
191
192
193
194
195
196
197 private Object parseArray(final MXParser parser, final TypeLiteral<?> toType) throws Exception {
198
199 final Collection<?> collection = parseCollection(parser, toType);
200 final Object array = Array.newInstance(toType.getRawType(), collection.size());
201
202 int i = 0;
203 for (final Object element : collection) {
204 Array.set(array, i++, element);
205 }
206
207 return array;
208 }
209
210
211
212
213
214
215
216 private Object parseBean(final MXParser parser, final TypeLiteral<?> toType, final Class<?> rawType)
217 throws Exception {
218 final Class<?> clazz = loadImplementation(parseImplementation(parser), rawType);
219
220
221 if (parser.next() == XmlPullParser.TEXT) {
222 final String text = parser.getText();
223
224
225 if (parser.next() != XmlPullParser.START_TAG) {
226 return convertText(text, clazz == rawType ? toType : TypeLiteral.get(clazz));
227 }
228 }
229
230 if (String.class == clazz) {
231
232 while (parser.getEventType() == XmlPullParser.START_TAG) {
233 final String pos = parser.getPositionDescription();
234 Logs.warn("Expected TEXT, not XML: {}", pos, new Throwable());
235 parser.skipSubTree();
236 parser.nextTag();
237 }
238 return "";
239 }
240
241 final Object bean = newImplementation(clazz);
242
243
244 final Map<String, BeanProperty<Object>> propertyMap = new HashMap<>();
245 for (final BeanProperty<Object> property : new BeanProperties(clazz)) {
246 final String name = property.getName();
247 if (!propertyMap.containsKey(name)) {
248 propertyMap.put(name, property);
249 }
250 }
251
252 while (parser.getEventType() == XmlPullParser.START_TAG) {
253
254 final BeanProperty<Object> property = propertyMap.get(Roles.camelizeName(parser.getName()));
255 if (property != null) {
256 property.set(bean, parse(parser, property.getType()));
257 parser.nextTag();
258 } else {
259 throw new XmlPullParserException("Unknown bean property: " + parser.getName(), parser, null);
260 }
261 }
262
263 return bean;
264 }
265
266
267
268
269
270
271
272 private static String parseImplementation(final XmlPullParser parser) {
273 return parser.getAttributeValue(null, "implementation");
274 }
275
276
277
278
279
280
281
282
283 private static Class<?> loadImplementation(final String name, final Class<?> defaultClazz) {
284 if (null == name) {
285 return defaultClazz;
286 }
287
288
289 final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
290 if (tccl != null) {
291 try {
292 return tccl.loadClass(name);
293 } catch (final Exception e) {
294
295 } catch (final LinkageError e) {
296
297 }
298 }
299
300
301 final ClassLoader peer = defaultClazz.getClassLoader();
302 if (peer != null) {
303 try {
304 return peer.loadClass(name);
305 } catch (final Exception e) {
306
307 } catch (final LinkageError e) {
308
309 }
310 }
311
312 try {
313
314 return Class.forName(name);
315 } catch (final Exception e) {
316 throw new TypeNotPresentException(name, e);
317 } catch (final LinkageError e) {
318 throw new TypeNotPresentException(name, e);
319 }
320 }
321
322
323
324
325
326
327
328 private static <T> T newImplementation(final Class<T> clazz) {
329 try {
330 return clazz.newInstance();
331 } catch (final Exception e) {
332 throw new IllegalArgumentException("Cannot create instance of: " + clazz, e);
333 } catch (final LinkageError e) {
334 throw new IllegalArgumentException("Cannot create instance of: " + clazz, e);
335 }
336 }
337
338
339
340
341
342
343
344
345 private static <T> T newImplementation(final Class<T> clazz, final String value) {
346 try {
347 return clazz.getConstructor(String.class).newInstance(value);
348 } catch (final Exception e) {
349 final Throwable cause = e instanceof InvocationTargetException ? e.getCause() : e;
350 throw new IllegalArgumentException(String.format(CONVERSION_ERROR, value, clazz), cause);
351 } catch (final LinkageError e) {
352 throw new IllegalArgumentException(String.format(CONVERSION_ERROR, value, clazz), e);
353 }
354 }
355
356
357
358
359
360
361
362
363 @SuppressWarnings("unchecked")
364 private static <T> T newImplementation(final XmlPullParser parser, final Class<T> defaultClazz) {
365 return (T) newImplementation(loadImplementation(parseImplementation(parser), defaultClazz));
366 }
367
368
369
370
371
372
373
374
375 private Object convertText(final String value, final TypeLiteral<?> toType) {
376 final String text = value.trim();
377
378 final Class<?> rawType = toType.getRawType();
379 if (rawType.isAssignableFrom(String.class)) {
380 return text;
381 }
382
383
384 final TypeLiteral<?> boxedType =
385 rawType.isPrimitive() ? Key.get(rawType).getTypeLiteral() : toType;
386
387 for (final TypeConverterBinding b : typeConverterBindings) {
388 if (b.getTypeMatcher().matches(boxedType)) {
389 return b.getTypeConverter().convert(text, toType);
390 }
391 }
392
393
394 return text.length() == 0 ? newImplementation(rawType) : newImplementation(rawType, text);
395 }
396 }