1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.content2bean.impl;
35
36 import info.magnolia.cms.core.Content;
37 import info.magnolia.cms.util.ContentUtil;
38 import info.magnolia.cms.util.SystemContentWrapper;
39 import info.magnolia.content2bean.Content2BeanException;
40 import info.magnolia.content2bean.Content2BeanTransformer;
41 import info.magnolia.content2bean.PropertyTypeDescriptor;
42 import info.magnolia.content2bean.TransformationState;
43 import info.magnolia.content2bean.TypeDescriptor;
44 import info.magnolia.content2bean.TypeMapping;
45 import info.magnolia.objectfactory.Classes;
46 import info.magnolia.objectfactory.ComponentProvider;
47
48 import java.lang.reflect.Method;
49 import java.util.Collection;
50 import java.util.Iterator;
51 import java.util.LinkedHashMap;
52 import java.util.Locale;
53 import java.util.Map;
54
55 import javax.inject.Inject;
56 import javax.inject.Singleton;
57 import javax.jcr.RepositoryException;
58
59 import org.apache.commons.beanutils.BeanUtilsBean;
60 import org.apache.commons.beanutils.MethodUtils;
61 import org.apache.commons.beanutils.PropertyUtils;
62 import org.apache.commons.beanutils.PropertyUtilsBean;
63 import org.apache.commons.lang3.LocaleUtils;
64 import org.apache.commons.lang3.StringUtils;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68
69
70
71
72
73 @Deprecated
74 @Singleton
75 public class Content2BeanTransformerImpl implements Content2BeanTransformer, Content.ContentFilter {
76
77 private static final Logger log = LoggerFactory.getLogger(Content2BeanTransformerImpl.class);
78
79 private final BeanUtilsBean beanUtilsBean;
80
81
82
83
84 @Inject
85 private TypeMapping typeMapping;
86
87 public Content2BeanTransformerImpl() {
88 super();
89
90
91
92
93
94 final EnumAwareConvertUtilsBean convertUtilsBean = new EnumAwareConvertUtilsBean();
95
96
97 convertUtilsBean.deregister(Class.class);
98
99 this.beanUtilsBean = new BeanUtilsBean(convertUtilsBean, new PropertyUtilsBean());
100 }
101
102 @Override
103 @Deprecated
104 public TypeDescriptor resolveType(TransformationState state) throws ClassNotFoundException {
105 throw new UnsupportedOperationException();
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119 @Override
120 public TypeDescriptor resolveType(TypeMapping typeMapping, TransformationState state, ComponentProvider componentProvider) throws ClassNotFoundException {
121 TypeDescriptor typeDscr = null;
122 Content node = state.getCurrentContent();
123
124 try {
125 if (node.hasNodeData("class")) {
126 String className = node.getNodeData("class").getString();
127 if (StringUtils.isBlank(className)) {
128 throw new ClassNotFoundException("(no value for class property)");
129 }
130 Class<?> clazz = Classes.getClassFactory().forName(className);
131 typeDscr = typeMapping.getTypeDescriptor(clazz);
132 }
133 } catch (RepositoryException e) {
134
135 log.warn("can't read class property", e);
136 }
137
138 if (typeDscr == null && state.getLevel() > 1) {
139 TypeDescriptor parentTypeDscr = state.getCurrentType();
140 PropertyTypeDescriptor propDscr;
141
142 if (parentTypeDscr.isMap() || parentTypeDscr.isCollection()) {
143 if (state.getLevel() > 2) {
144
145 String mapProperyName = state.peekContent(1).getName();
146 propDscr = state.peekType(1).getPropertyTypeDescriptor(mapProperyName, typeMapping);
147 if (propDscr != null) {
148 typeDscr = propDscr.getCollectionEntryType();
149 }
150 }
151 } else {
152 propDscr = state.getCurrentType().getPropertyTypeDescriptor(node.getName(), typeMapping);
153 if (propDscr != null) {
154 typeDscr = propDscr.getType();
155 }
156 }
157 }
158
159 typeDscr = onResolveType(typeMapping, state, typeDscr, componentProvider);
160
161 if (typeDscr != null) {
162
163 final Class<?> type = typeDscr.getType();
164 typeDscr = typeMapping.getTypeDescriptor(componentProvider.getImplementation(type));
165
166
167 Content2BeanTransformer customTransformer = typeDscr.getTransformer();
168 if (customTransformer != null && customTransformer != this) {
169 TypeDescriptor typeFoundByCustomTransformer = customTransformer.resolveType(typeMapping, state, componentProvider);
170
171
172 if (typeFoundByCustomTransformer != TypeMapping.MAP_TYPE) {
173
174 Class<?> implementation = componentProvider.getImplementation(typeFoundByCustomTransformer.getType());
175 typeDscr = typeMapping.getTypeDescriptor(implementation);
176 }
177 }
178 }
179
180 if (typeDscr == null || typeDscr.needsDefaultMapping()) {
181 if (typeDscr == null) {
182 log.debug("was not able to resolve type for node [{}] will use a map", node);
183 }
184 typeDscr = TypeMapping.MAP_TYPE;
185 }
186
187 log.debug("{} --> {}", node.getHandle(), typeDscr.getType());
188
189 return typeDscr;
190 }
191
192
193
194
195
196 protected TypeDescriptor onResolveType(TypeMapping typeMapping, TransformationState state, TypeDescriptor resolvedType, ComponentProvider componentProvider) {
197 return resolvedType;
198 }
199
200
201
202
203 protected TypeDescriptor onResolveType(TransformationState state, TypeDescriptor resolvedType, ComponentProvider componentProvider) {
204 return onResolveType(getTypeMapping(), state, resolvedType, componentProvider);
205 }
206
207 @Override
208 public Collection<Content> getChildren(Content node) {
209 return node.getChildren(this);
210 }
211
212
213
214
215 @Override
216 public boolean accept(Content content) {
217 return ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER.accept(content);
218 }
219
220 @Override
221 public void setProperty(TransformationState state, PropertyTypeDescriptor descriptor, Map<String, Object> values) {
222 throw new UnsupportedOperationException();
223 }
224
225
226
227
228 @Override
229 public void setProperty(TypeMapping mapping, TransformationState state, PropertyTypeDescriptor descriptor, Map<String, Object> values) {
230 String propertyName = descriptor.getName();
231 if (propertyName.equals("class")) {
232 return;
233 }
234 Object value = values.get(propertyName);
235 Object bean = state.getCurrentBean();
236
237 if (propertyName.equals("content") && value == null) {
238 value = new SystemContentWrapper(state.getCurrentContent());
239 } else if (propertyName.equals("name") && value == null) {
240 value = state.getCurrentContent().getName();
241 } else if (propertyName.equals("className") && value == null) {
242 value = values.get("class");
243 }
244
245
246
247 if (value == null) {
248 return;
249 }
250
251 log.debug("try to set {}.{} with value {}", bean, propertyName, value);
252
253
254 if (!(bean instanceof Map)) {
255 try {
256 PropertyTypeDescriptor dscr = mapping.getPropertyTypeDescriptor(bean.getClass(), propertyName);
257 if (dscr.getType() != null) {
258
259
260 if (dscr.isCollection() || dscr.isMap()) {
261 log.debug("{} is of type collection, map or /array", propertyName);
262 Method method = dscr.getAddMethod();
263
264 if (method != null) {
265 log.debug("clearing the current content of the collection/map");
266 try {
267 Object col = PropertyUtils.getProperty(bean, propertyName);
268 if (col != null) {
269 MethodUtils.invokeExactMethod(col, "clear", new Object[]{});
270 }
271 } catch (Exception e) {
272 log.debug("no clear method found on collection {}", propertyName);
273 }
274
275 Class<?> entryClass = dscr.getCollectionEntryType().getType();
276
277 log.debug("will add values by using adder method {}", method.getName());
278 for (Iterator<Object> iter = ((Map<Object, Object>) value).keySet().iterator(); iter
279 .hasNext(); ) {
280 Object key = iter.next();
281 Object entryValue = ((Map<Object, Object>) value).get(key);
282 entryValue = convertPropertyValue(entryClass, entryValue);
283 if (entryClass.isAssignableFrom(entryValue.getClass())) {
284 if (dscr.isCollection()) {
285 log.debug("will add value {}", entryValue);
286 method.invoke(bean, entryValue);
287 }
288
289 else {
290 log.debug("will add key {} with value {}", key, entryValue);
291 method.invoke(bean, key, entryValue);
292 }
293 }
294 }
295
296 return;
297 }
298 log.debug("no add method found for property {}", propertyName);
299 if (dscr.isCollection()) {
300 log.debug("transform the values to a collection", propertyName);
301 value = ((Map<Object, Object>) value).values();
302 }
303 } else {
304 value = convertPropertyValue(dscr.getType().getType(), value);
305 }
306 }
307 } catch (Exception e) {
308
309 log.error("Can't set property [{}] to value [{}] in bean [{}] for node {} due to {}", propertyName, value, bean.getClass().getName(), state.getCurrentContent().getHandle(), e.toString());
310 log.debug("stacktrace", e);
311 }
312 }
313
314 try {
315
316
317
318
319
320
321 beanUtilsBean.setProperty(bean, propertyName, value);
322
323
324
325 } catch (Exception e) {
326
327 log.error("Can't set property [{}] to value [{}] in bean [{}] for node {} due to {}", propertyName, value, bean.getClass().getName(), state.getCurrentContent().getHandle(), e.toString());
328 log.debug("stacktrace", e);
329 }
330
331 }
332
333
334
335
336
337 @Override
338 public Object convertPropertyValue(Class<?> propertyType, Object value) throws Content2BeanException {
339 if (Class.class.equals(propertyType)) {
340 try {
341 return Classes.getClassFactory().forName(value.toString());
342 } catch (ClassNotFoundException e) {
343 log.error(e.getMessage());
344 throw new Content2BeanException(e);
345 }
346 }
347
348 if (Locale.class.equals(propertyType)) {
349 if (value instanceof String) {
350 String localeStr = (String) value;
351 if (StringUtils.isNotEmpty(localeStr)) {
352 return LocaleUtils.toLocale(localeStr);
353 }
354 }
355 }
356
357 if (Collection.class.equals(propertyType) && value instanceof Map) {
358
359 return ((Map) value).values();
360 }
361
362
363 if (String.class.equals(propertyType) && value instanceof Map && ((Map) value).size() == 1) {
364 return ((Map) value).values().iterator().next();
365 }
366
367 return value;
368 }
369
370
371
372
373 @Override
374 public Object newBeanInstance(TransformationState state, Map properties, ComponentProvider componentProvider) throws Content2BeanException {
375
376
377 final Object bean = convertPropertyValue(state.getCurrentType().getType(), properties);
378
379 if (bean == properties) {
380 try {
381
382
383 final Class<?> type = state.getCurrentType().getType();
384 if (LinkedHashMap.class.equals(type)) {
385
386
387 return new LinkedHashMap();
388 } else if (Map.class.isAssignableFrom(type)) {
389
390 log.warn("someone wants another type of map ? {}", type);
391 }
392 return componentProvider.newInstance(type);
393 } catch (Throwable e) {
394 throw new Content2BeanException(e);
395 }
396 }
397 return bean;
398 }
399
400
401
402
403 @Override
404 public void initBean(TransformationState state, Map properties) throws Content2BeanException {
405 Object bean = state.getCurrentBean();
406
407 Method init;
408 try {
409 init = bean.getClass().getMethod("init", new Class[]{});
410 try {
411 init.invoke(bean);
412 } catch (Exception e) {
413 throw new Content2BeanException("can't call init method", e);
414 }
415 } catch (SecurityException e) {
416 return;
417 } catch (NoSuchMethodException e) {
418 return;
419 }
420 log.debug("{} is initialized", bean);
421 }
422
423 @Override
424 public TransformationState newState() {
425 return new TransformationStateImpl();
426
427
428
429 }
430
431
432
433
434
435
436 @Override
437 public TypeMapping getTypeMapping() {
438 return typeMapping;
439 }
440
441 }