missing implementations for the generic controller architeture (works for data portal...
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / json / processor / bean / AbstractCdmBeanProcessor.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
31 import eu.etaxonomy.cdm.model.common.CdmBase;
32 import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer;
33
34 /**
35 * @author a.kohlbecker
36 * @date 30.03.2009
37 *
38 */
39 public abstract class AbstractCdmBeanProcessor<T extends CdmBase> implements JsonBeanProcessor{
40
41 public static final Logger logger = Logger.getLogger(AbstractCdmBeanProcessor.class);
42
43 private Set<String> excludes = new HashSet<String>();
44
45 public Set<String> getExcludes() {
46 return excludes;
47 }
48
49 /**
50 * This method allows supplying a List of property names to be ignored
51 * during the serialization to JSON. The <code>excludes</code> will be
52 * merged with the property names configured by subclasses which override
53 * {@link {@link #getIgnorePropNames()}.
54 *
55 * @param excludes
56 */
57 public void setExcludes(Set<String> excludes) {
58 this.excludes = excludes;
59 }
60
61 /**
62 * Implementations of this abstract class may override this method in order
63 * to supply a List of property names to be ignored in
64 * {@link #processBean(Object, JsonConfig)}. This feature generally is used
65 * when {@link #processBeanSecondStep(CdmBase, JSONObject, JsonConfig)} is
66 * implemented. such that this method is responsible of serializing this
67 * property.
68 *
69 * @return a List of property names.
70 */
71 public abstract List<String> getIgnorePropNames();
72
73 /**
74 * merges and returns {@link {@link #getIgnorePropNames()} with
75 * {@link #excludes}
76 *
77 * @return
78 */
79 protected Set<String> getMergedExcludes(){
80 Set<String> mergedExcludes = new HashSet<String>(excludes);
81 if(getIgnorePropNames() != null){
82 mergedExcludes.addAll(getIgnorePropNames());
83 }
84 return mergedExcludes;
85 }
86
87 /* (non-Javadoc)
88 * @see net.sf.json.processors.JsonBeanProcessor#processBean(java.lang.Object, net.sf.json.JsonConfig)
89 */
90 public final JSONObject processBean(Object bean, JsonConfig jsonConfig) {
91
92 if(logger.isDebugEnabled()){
93 logger.debug("processing " + bean.getClass());
94 }
95
96 JSONObject json = new JSONObject();
97 Collection exclusions = jsonConfig.getMergedExcludes( bean.getClass() );
98 Set<Class> typeRestrictions = new HashSet<Class>();
99 typeRestrictions.add(CdmBase.class);
100 Set<PropertyDescriptor> props = AbstractBeanInitializer.getProperties(bean, null);
101 PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
102 for(PropertyDescriptor prop: props){
103 String key = prop.getName();
104 if(getMergedExcludes().contains(key) || exclusions.contains(key)){
105 if(logger.isDebugEnabled()){
106 logger.debug("skipping excluded property " + key);
107 }
108 continue;
109 }
110
111 try {
112 // ------ reusing snippet from JSONOnbject._fromBean()
113 Class type = prop.getPropertyType();
114 Object value = PropertyUtils.getProperty( bean, key );
115
116 if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
117 continue;
118 }
119 JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(bean.getClass(), type, key );
120 if( jsonValueProcessor != null ){
121 value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
122 if( !JsonVerifier.isValidJsonValue( value ) ){
123 throw new JSONException( "Value is not a valid JSON value. " + value );
124 }
125 }
126 // ----- END of snipped
127 if(logger.isDebugEnabled()){
128 logger.debug("processing " + key + " of " + bean.getClass());
129 }
130 if(CdmBase.class.isAssignableFrom(type)){
131 json.element(key, value, jsonConfig);
132 } else if(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)){
133 json.element(key, value, jsonConfig);
134 } else if(Object.class.isAssignableFrom(type)){
135 json.element(key, value, jsonConfig);
136 } else {
137 json.element(key, value);
138 }
139
140 } catch (IllegalAccessException e) {
141 logger.error(e.getMessage(), e);
142 } catch (InvocationTargetException e) {
143 logger.error(e.getMessage(), e);
144 } catch (NoSuchMethodException e) {
145 logger.error(e.getMessage(), e);
146 }
147 }
148
149 json = processBeanSecondStep((T) bean, json, jsonConfig);
150
151 return json;
152 }
153
154 /**
155 * This method is called ate the end of {@link #processBean(Object, JsonConfig)} just before the JSONObject is returned.
156 * By overriding this method it is possible to to further processing.
157 * <p>
158 * <b>See also {@link #getIgnorePropNames()}!</b>
159 *
160 * @param bean
161 * @param json
162 * @param jsonConfig
163 * @return
164 */
165 public abstract JSONObject processBeanSecondStep(T bean, JSONObject json, JsonConfig jsonConfig) ;
166
167
168 }