package org.epics.archiverappliance.utils.ui;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.common.TimeUtils;
import org.epics.archiverappliance.config.ArchDBRTypes;
import org.epics.archiverappliance.mgmt.policy.PolicyConfig.SamplingMethod;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/**
* Generate something that converts a POJO into JSON using bean introspection.
* Underlying framework is still json-simple.
* This has a giant switch statement that does things based on types; so please add unit tests as part of adding new fields to objects that use JSONEncoder.
* @author mshankar
*
*/
public class JSONEncoder<T> {
private static Logger logger = Logger.getLogger(JSONEncoder.class.getName());
public static <T> JSONEncoder<T> getEncoder(Class<T> clazz) throws IntrospectionException {
return new JSONEncoder<T>(clazz);
}
private LinkedList<AttributeEncoder> encoders = new LinkedList<AttributeEncoder>();
private JSONEncoder(Class<T> clazz) throws IntrospectionException {
BeanInfo info = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
for(PropertyDescriptor descriptor : descriptors) {
if(descriptor.getPropertyType().equals(String.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(boolean.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(int.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(long.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(float.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(double.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(Double.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(Long.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(Timestamp.class)) {
encoders.add(new ISO8601Encoder(descriptor));
} else if(descriptor.getPropertyType().equals(ArchDBRTypes.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(SamplingMethod.class)) {
encoders.add(new ToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(String[].class)) {
encoders.add(new ArrayOfToStringEncoder(descriptor));
} else if(descriptor.getPropertyType().equals(HashMap.class)) {
encoders.add(new HashMapEncoder(descriptor));
} else if(descriptor.getName().equals("class")) {
// Skip class...
} else {
throw new IntrospectionException("Do not have JSON encoder for property " + descriptor.getName() + " of type " + descriptor.getPropertyType().getCanonicalName());
}
}
}
private static interface AttributeEncoder {
String getProperty();
void encode(Object obj, JSONObject jsonObj) throws IllegalAccessException, InvocationTargetException ;
}
public JSONObject encode(T obj) throws IllegalAccessException, InvocationTargetException {
JSONObject jsonObj = new JSONObject();
for(AttributeEncoder encoder : encoders) {
try {
encoder.encode(obj, jsonObj);
} catch(Exception ex) {
logger.error("Exception marshalling attribute " + encoder.getProperty(), ex);
}
}
return jsonObj;
}
/**
* Encode the object and add it to this array
* Dealing with JSON generates a lot of suppress warnings from raw types..
* @param obj T
* @param arrayOfObjs JSONArray
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@SuppressWarnings("unchecked")
public void encodeAndAdd(T obj, JSONArray arrayOfObjs) throws IllegalAccessException, InvocationTargetException {
JSONObject jsonObj = this.encode(obj);
arrayOfObjs.add(jsonObj);
}
public void encodeAndPrint(T obj, PrintWriter out) throws IllegalAccessException, InvocationTargetException {
JSONObject jsonObj = this.encode(obj);
out.print(JSONValue.toJSONString(jsonObj));
}
private static class ToStringEncoder implements AttributeEncoder {
private String propertyName;
private Method readMethod;
private ToStringEncoder(PropertyDescriptor descriptor) {
propertyName = descriptor.getName();
readMethod = descriptor.getReadMethod();
}
/* (non-Javadoc)
* @see org.epics.archiverappliance.utils.ui.JSONEncoder.AttributeEncoder#encode(java.lang.Object, org.json.simple.JSONObject)
* Dealing with JSON generates a lot of suppress warnings from raw types..
*/
@SuppressWarnings("unchecked")
@Override
public void encode(Object obj, JSONObject jsonObj) throws IllegalAccessException, InvocationTargetException {
Object val = readMethod.invoke(obj);
if(val != null) {
jsonObj.put(propertyName, val.toString());
}
}
@Override
public String getProperty() {
return propertyName;
}
}
private static class ArrayOfToStringEncoder implements AttributeEncoder {
private String propertyName;
private Method readMethod;
private ArrayOfToStringEncoder(PropertyDescriptor descriptor) {
propertyName = descriptor.getName();
readMethod = descriptor.getReadMethod();
}
/* (non-Javadoc)
* @see org.epics.archiverappliance.utils.ui.JSONEncoder.AttributeEncoder#encode(java.lang.Object, org.json.simple.JSONObject)
* Dealing with JSON generates a lot of suppress warnings from raw types..
*/
@SuppressWarnings("unchecked")
@Override
public void encode(Object obj, JSONObject jsonObj) throws IllegalAccessException, InvocationTargetException {
Object[] vals = (Object[]) readMethod.invoke(obj);
if(vals != null) {
JSONArray valarray = new JSONArray();
for(Object val : vals) {
valarray.add(val.toString());
}
jsonObj.put(propertyName, valarray);
}
}
@Override
public String getProperty() {
return propertyName;
}
}
private static class ISO8601Encoder implements AttributeEncoder {
private String propertyName;
private Method readMethod;
private ISO8601Encoder(PropertyDescriptor descriptor) {
propertyName = descriptor.getName();
readMethod = descriptor.getReadMethod();
}
/* (non-Javadoc)
* @see org.epics.archiverappliance.utils.ui.JSONEncoder.AttributeEncoder#encode(java.lang.Object, org.json.simple.JSONObject)
* Dealing with JSON generates a lot of suppress warnings from raw types..
*/
@SuppressWarnings("unchecked")
@Override
public void encode(Object obj, JSONObject jsonObj) throws IllegalAccessException, InvocationTargetException {
Timestamp ts = (Timestamp) readMethod.invoke(obj);
if(ts != null) {
jsonObj.put(propertyName, TimeUtils.convertToISO8601String(ts));
}
}
@Override
public String getProperty() {
return propertyName;
}
}
private static class HashMapEncoder implements AttributeEncoder {
private String propertyName;
private Method readMethod;
private HashMapEncoder(PropertyDescriptor descriptor) {
propertyName = descriptor.getName();
readMethod = descriptor.getReadMethod();
}
/* (non-Javadoc)
* @see org.epics.archiverappliance.utils.ui.JSONEncoder.AttributeEncoder#encode(java.lang.Object, org.json.simple.JSONObject)
* Dealing with JSON generates a lot of suppress warnings from raw types..
*/
@SuppressWarnings("unchecked")
@Override
public void encode(Object obj, JSONObject jsonObj) throws IllegalAccessException, InvocationTargetException {
HashMap<String, Object> childMap = (HashMap<String, Object>) readMethod.invoke(obj);
if(childMap != null) {
JSONObject childObj = new JSONObject();
for(String key : childMap.keySet()) {
childObj.put(key, childMap.get(key).toString());
}
jsonObj.put(propertyName, childObj);
}
}
@Override
public String getProperty() {
return propertyName;
}
}
}