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