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