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