d9d77920eef976dda692a0d18f8042dfba3987b3
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / json / processor / bean / AbstractBeanProcessor.java
1 /**
2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.remote.json.processor.bean;
10
11 import java.beans.PropertyDescriptor;
12 import java.lang.reflect.InvocationTargetException;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.apache.commons.beanutils.PropertyUtils;
20 import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
21 import org.hibernate.Hibernate;
22
23 import eu.etaxonomy.cdm.model.common.CdmBase;
24 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
25 import net.sf.json.JSONException;
26 import net.sf.json.JSONObject;
27 import net.sf.json.JsonConfig;
28 import net.sf.json.processors.JsonBeanProcessor;
29 import net.sf.json.processors.JsonValueProcessor;
30 import net.sf.json.processors.JsonVerifier;
31 import net.sf.json.util.PropertyFilter;
32
33 /**
34 * @author a.kohlbecker
35 * @since 30.03.2009
36 */
37 public abstract class AbstractBeanProcessor<T extends Object> implements JsonBeanProcessor{
38
39 public static final Logger logger = LogManager.getLogger(AbstractBeanProcessor.class);
40
41 private Set<String> excludes = new HashSet<>();
42
43 private Set<String> mergedExcludes = null;
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 if(mergedExcludes == null) {
81 mergedExcludes = new HashSet<>(excludes);
82 if(getIgnorePropNames() != null){
83 mergedExcludes.addAll(getIgnorePropNames());
84 }
85 }
86
87 return mergedExcludes;
88 }
89
90 protected void addJsonElement(JSONObject json, JsonConfig jsonConfig, String fieldName, Object fieldObject) {
91 if(Hibernate.isInitialized(fieldObject)){
92 json.element(fieldName, fieldObject, jsonConfig);
93 }
94 }
95
96 @Override
97 public final JSONObject processBean(Object bean, JsonConfig jsonConfig) {
98
99 if(logger.isDebugEnabled()){
100 logger.debug("processing " + bean.getClass());
101 }
102
103 JSONObject json = new JSONObject();
104 Collection<?> exclusions = jsonConfig.getMergedExcludes( bean.getClass() );
105 Set<PropertyDescriptor> props = AbstractBeanInitializer.getProperties(bean, null);
106 PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
107 for(PropertyDescriptor prop: props){
108 String key = prop.getName();
109 if(getMergedExcludes().contains(key) || exclusions.contains(key)){
110 if(logger.isDebugEnabled()){
111 logger.debug("skipping excluded property " + key);
112 }
113 continue;
114 }
115
116 try {
117 // ------ reusing snippet from JSONOnbject._fromBean()
118 Class<?> type = prop.getPropertyType();
119 Object value = PropertyUtils.getProperty( bean, key );
120
121 if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
122 continue;
123 }
124 JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(bean.getClass(), type, key );
125 if( jsonValueProcessor != null ){
126 value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
127 if( !JsonVerifier.isValidJsonValue( value ) ){
128 throw new JSONException( "Value is not a valid JSON value. " + value );
129 }
130 }
131 // ----- END of snipped
132 if(logger.isDebugEnabled()){
133 logger.debug("processing " + key + " of " + bean.getClass());
134 }
135 if(CdmBase.class.isAssignableFrom(type)){
136 json.element(key, value, jsonConfig);
137 } else if(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)){
138 json.element(key, value, jsonConfig);
139 } else if(Object.class.isAssignableFrom(type)){
140 json.element(key, value, jsonConfig);
141 } else {
142 json.element(key, value);
143 }
144
145 } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
146 logger.error(e.getMessage(), e);
147 }
148 }
149
150 json = processBeanSecondStep((T) bean, json, jsonConfig);
151
152 return json;
153 }
154
155 /**
156 * This method is called at the end of {@link #processBean(Object, JsonConfig)} just before the JSONObject is returned.
157 * By overriding this method it is possible to do further processing.
158 * <p>
159 * <b>See also {@link #getIgnorePropNames()}!</b>
160 */
161 public abstract JSONObject processBeanSecondStep(T bean, JSONObject json, JsonConfig jsonConfig) ;
162 }