moving all beaninitializer classes to new package 'eu.etaxonomy.cdm.persistence.dao...
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / json / processor / bean / AbstractBeanProcessor.java
1 // $Id$
2 /**
3 * Copyright (C) 2009 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.cdm.remote.json.processor.bean;
11
12 import java.beans.PropertyDescriptor;
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19
20 import net.sf.json.JSONException;
21 import net.sf.json.JSONObject;
22 import net.sf.json.JsonConfig;
23 import net.sf.json.processors.JsonBeanProcessor;
24 import net.sf.json.processors.JsonValueProcessor;
25 import net.sf.json.processors.JsonVerifier;
26 import net.sf.json.util.PropertyFilter;
27
28 import org.apache.commons.beanutils.PropertyUtils;
29 import org.apache.log4j.Logger;
30 import org.hibernate.Hibernate;
31
32 import eu.etaxonomy.cdm.model.common.CdmBase;
33 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
34
35 /**
36 * @author a.kohlbecker
37 * @date 30.03.2009
38 *
39 */
40 public abstract class AbstractBeanProcessor<T extends Object> implements JsonBeanProcessor{
41
42 public static final Logger logger = Logger.getLogger(AbstractBeanProcessor.class);
43
44 private Set<String> excludes = new HashSet<String>();
45
46 public Set<String> getExcludes() {
47 return excludes;
48 }
49
50 /**
51 * This method allows supplying a List of property names to be ignored
52 * during the serialization to JSON. The <code>excludes</code> will be
53 * merged with the property names configured by subclasses which override
54 * {@link {@link #getIgnorePropNames()}.
55 *
56 * @param excludes
57 */
58 public void setExcludes(Set<String> excludes) {
59 this.excludes = excludes;
60 }
61
62 /**
63 * Implementations of this abstract class may override this method in order
64 * to supply a List of property names to be ignored in
65 * {@link #processBean(Object, JsonConfig)}. This feature generally is used
66 * when {@link #processBeanSecondStep(CdmBase, JSONObject, JsonConfig)} is
67 * implemented. such that this method is responsible of serializing this
68 * property.
69 *
70 * @return a List of property names.
71 */
72 public abstract List<String> getIgnorePropNames();
73
74 /**
75 * merges and returns {@link {@link #getIgnorePropNames()} with
76 * {@link #excludes}
77 *
78 * @return
79 */
80 protected Set<String> getMergedExcludes(){
81 Set<String> mergedExcludes = new HashSet<String>(excludes);
82 if(getIgnorePropNames() != null){
83 mergedExcludes.addAll(getIgnorePropNames());
84 }
85 return mergedExcludes;
86 }
87
88 /**
89 *
90 * @param json
91 * @param jsonConfig
92 * @param fieldName
93 * @param fieldObject
94 */
95 protected void addJsonElement(JSONObject json, JsonConfig jsonConfig, String fieldName, Object fieldObject) {
96 if(Hibernate.isInitialized(fieldObject)){
97 json.element(fieldName, fieldObject, jsonConfig);
98 }
99 }
100
101
102 /* (non-Javadoc)
103 * @see net.sf.json.processors.JsonBeanProcessor#processBean(java.lang.Object, net.sf.json.JsonConfig)
104 */
105 public final JSONObject processBean(Object bean, JsonConfig jsonConfig) {
106
107 if(logger.isDebugEnabled()){
108 logger.debug("processing " + bean.getClass());
109 }
110
111 JSONObject json = new JSONObject();
112 Collection exclusions = jsonConfig.getMergedExcludes( bean.getClass() );
113 Set<Class> typeRestrictions = new HashSet<Class>();
114 typeRestrictions.add(CdmBase.class);
115 Set<PropertyDescriptor> props = AbstractBeanInitializer.getProperties(bean, null);
116 PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
117 for(PropertyDescriptor prop: props){
118 String key = prop.getName();
119 if(getMergedExcludes().contains(key) || exclusions.contains(key)){
120 if(logger.isDebugEnabled()){
121 logger.debug("skipping excluded property " + key);
122 }
123 continue;
124 }
125
126 try {
127 // ------ reusing snippet from JSONOnbject._fromBean()
128 Class type = prop.getPropertyType();
129 Object value = PropertyUtils.getProperty( bean, key );
130
131 if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
132 continue;
133 }
134 JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(bean.getClass(), type, key );
135 if( jsonValueProcessor != null ){
136 value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
137 if( !JsonVerifier.isValidJsonValue( value ) ){
138 throw new JSONException( "Value is not a valid JSON value. " + value );
139 }
140 }
141 // ----- END of snipped
142 if(logger.isDebugEnabled()){
143 logger.debug("processing " + key + " of " + bean.getClass());
144 }
145 if(CdmBase.class.isAssignableFrom(type)){
146 json.element(key, value, jsonConfig);
147 } else if(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)){
148 json.element(key, value, jsonConfig);
149 } else if(Object.class.isAssignableFrom(type)){
150 json.element(key, value, jsonConfig);
151 } else {
152 json.element(key, value);
153 }
154
155 } catch (IllegalAccessException e) {
156 logger.error(e.getMessage(), e);
157 } catch (InvocationTargetException e) {
158 logger.error(e.getMessage(), e);
159 } catch (NoSuchMethodException e) {
160 logger.error(e.getMessage(), e);
161 }
162 }
163
164 json = processBeanSecondStep((T) bean, json, jsonConfig);
165
166 return json;
167 }
168
169 /**
170 * This method is called ate the end of {@link #processBean(Object, JsonConfig)} just before the JSONObject is returned.
171 * By overriding this method it is possible to to further processing.
172 * <p>
173 * <b>See also {@link #getIgnorePropNames()}!</b>
174 *
175 * @param bean
176 * @param json
177 * @param jsonConfig
178 * @return
179 */
180 public abstract JSONObject processBeanSecondStep(T bean, JSONObject json, JsonConfig jsonConfig) ;
181
182
183 }