Mercurial Hosting > luan
diff src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java Wed Sep 07 21:15:48 2016 -0600 @@ -0,0 +1,431 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util.ajax; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.util.ajax.JSON.Output; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +/** + * Converts POJOs to JSON and vice versa. + * The key difference: + * - returns the actual object from Convertor.fromJSON (JSONObjectConverter returns a Map) + * - the getters/setters are resolved at initialization (JSONObjectConverter resolves it at runtime) + * - correctly sets the number fields + * + */ +public class JSONPojoConvertor implements JSON.Convertor +{ + private static final Logger LOG = Log.getLogger(JSONPojoConvertor.class); + public static final Object[] GETTER_ARG = new Object[]{}, NULL_ARG = new Object[]{null}; + private static final Map<Class<?>, NumberType> __numberTypes = new HashMap<Class<?>, NumberType>(); + + public static NumberType getNumberType(Class<?> clazz) + { + return __numberTypes.get(clazz); + } + + protected boolean _fromJSON; + protected Class<?> _pojoClass; + protected Map<String,Method> _getters = new HashMap<String,Method>(); + protected Map<String,Setter> _setters = new HashMap<String,Setter>(); + protected Set<String> _excluded; + + /** + * @param pojoClass The class to convert + */ + public JSONPojoConvertor(Class<?> pojoClass) + { + this(pojoClass, (Set<String>)null, true); + } + + /** + * @param pojoClass The class to convert + * @param excluded The fields to exclude + */ + public JSONPojoConvertor(Class<?> pojoClass, String[] excluded) + { + this(pojoClass, new HashSet<String>(Arrays.asList(excluded)), true); + } + + /** + * @param pojoClass The class to convert + * @param excluded The fields to exclude + */ + public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded) + { + this(pojoClass, excluded, true); + } + + /** + * @param pojoClass The class to convert + * @param excluded The fields to exclude + * @param fromJSON If true, add a class field to the JSON + */ + public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded, boolean fromJSON) + { + _pojoClass = pojoClass; + _excluded = excluded; + _fromJSON = fromJSON; + init(); + } + + /** + * @param pojoClass The class to convert + * @param fromJSON If true, add a class field to the JSON + */ + public JSONPojoConvertor(Class<?> pojoClass, boolean fromJSON) + { + this(pojoClass, (Set<String>)null, fromJSON); + } + + /* ------------------------------------------------------------ */ + protected void init() + { + Method[] methods = _pojoClass.getMethods(); + for (int i=0;i<methods.length;i++) + { + Method m=methods[i]; + if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass()!=Object.class) + { + String name=m.getName(); + switch(m.getParameterTypes().length) + { + case 0: + + if(m.getReturnType()!=null) + { + if (name.startsWith("is") && name.length()>2) + name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3); + else if (name.startsWith("get") && name.length()>3) + name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); + else + break; + if(includeField(name, m)) + addGetter(name, m); + } + break; + case 1: + if (name.startsWith("set") && name.length()>3) + { + name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4); + if(includeField(name, m)) + addSetter(name, m); + } + break; + } + } + } + } + + /* ------------------------------------------------------------ */ + protected void addGetter(String name, Method method) + { + _getters.put(name, method); + } + + /* ------------------------------------------------------------ */ + protected void addSetter(String name, Method method) + { + _setters.put(name, new Setter(name, method)); + } + + /* ------------------------------------------------------------ */ + protected Setter getSetter(String name) + { + return _setters.get(name); + } + + /* ------------------------------------------------------------ */ + protected boolean includeField(String name, Method m) + { + return _excluded==null || !_excluded.contains(name); + } + + /* ------------------------------------------------------------ */ + protected int getExcludedCount() + { + return _excluded==null ? 0 : _excluded.size(); + } + + /* ------------------------------------------------------------ */ + public Object fromJSON(Map object) + { + Object obj = null; + try + { + obj = _pojoClass.newInstance(); + } + catch(Exception e) + { + // TODO return Map instead? + throw new RuntimeException(e); + } + + setProps(obj, object); + return obj; + } + + /* ------------------------------------------------------------ */ + public int setProps(Object obj, Map<?,?> props) + { + int count = 0; + for(Iterator<?> iterator = props.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry<?, ?> entry = (Map.Entry<?,?>) iterator.next(); + Setter setter = getSetter((String)entry.getKey()); + if(setter!=null) + { + try + { + setter.invoke(obj, entry.getValue()); + count++; + } + catch(Exception e) + { + // TODO throw exception? + LOG.warn(_pojoClass.getName()+"#"+setter.getPropertyName()+" not set from "+ + (entry.getValue().getClass().getName())+"="+entry.getValue().toString()); + log(e); + } + } + } + return count; + } + + /* ------------------------------------------------------------ */ + public void toJSON(Object obj, Output out) + { + if(_fromJSON) + out.addClass(_pojoClass); + for(Map.Entry<String,Method> entry : _getters.entrySet()) + { + try + { + out.add(entry.getKey(), entry.getValue().invoke(obj, GETTER_ARG)); + } + catch(Exception e) + { + // TODO throw exception? + LOG.warn("{} property '{}' excluded. (errors)", _pojoClass.getName(), + entry.getKey()); + log(e); + } + } + } + + /* ------------------------------------------------------------ */ + protected void log(Throwable t) + { + LOG.ignore(t); + } + + /* ------------------------------------------------------------ */ + public static class Setter + { + protected String _propertyName; + protected Method _setter; + protected NumberType _numberType; + protected Class<?> _type; + protected Class<?> _componentType; + + public Setter(String propertyName, Method method) + { + _propertyName = propertyName; + _setter = method; + _type = method.getParameterTypes()[0]; + _numberType = __numberTypes.get(_type); + if(_numberType==null && _type.isArray()) + { + _componentType = _type.getComponentType(); + _numberType = __numberTypes.get(_componentType); + } + } + + public String getPropertyName() + { + return _propertyName; + } + + public Method getMethod() + { + return _setter; + } + + public NumberType getNumberType() + { + return _numberType; + } + + public Class<?> getType() + { + return _type; + } + + public Class<?> getComponentType() + { + return _componentType; + } + + public boolean isPropertyNumber() + { + return _numberType!=null; + } + + public void invoke(Object obj, Object value) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException + { + if(value==null) + _setter.invoke(obj, NULL_ARG); + else + invokeObject(obj, value); + } + + protected void invokeObject(Object obj, Object value) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException + { + + if (_type.isEnum()) + { + if (value instanceof Enum) + _setter.invoke(obj, new Object[]{value}); + else + _setter.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())}); + } + else if(_numberType!=null && value instanceof Number) + { + _setter.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)}); + } + else if (Character.TYPE.equals(_type) || Character.class.equals(_type)) + { + _setter.invoke(obj, new Object[]{String.valueOf(value).charAt(0)}); + } + else if(_componentType!=null && value.getClass().isArray()) + { + if(_numberType==null) + { + int len = Array.getLength(value); + Object array = Array.newInstance(_componentType, len); + try + { + System.arraycopy(value, 0, array, 0, len); + } + catch(Exception e) + { + // unusual array with multiple types + LOG.ignore(e); + _setter.invoke(obj, new Object[]{value}); + return; + } + _setter.invoke(obj, new Object[]{array}); + } + else + { + Object[] old = (Object[])value; + Object array = Array.newInstance(_componentType, old.length); + try + { + for(int i=0; i<old.length; i++) + Array.set(array, i, _numberType.getActualValue((Number)old[i])); + } + catch(Exception e) + { + // unusual array with multiple types + LOG.ignore(e); + _setter.invoke(obj, new Object[]{value}); + return; + } + _setter.invoke(obj, new Object[]{array}); + } + } + else + _setter.invoke(obj, new Object[]{value}); + } + } + + public interface NumberType + { + public Object getActualValue(Number number); + } + + public static final NumberType SHORT = new NumberType() + { + public Object getActualValue(Number number) + { + return new Short(number.shortValue()); + } + }; + + public static final NumberType INTEGER = new NumberType() + { + public Object getActualValue(Number number) + { + return new Integer(number.intValue()); + } + }; + + public static final NumberType FLOAT = new NumberType() + { + public Object getActualValue(Number number) + { + return new Float(number.floatValue()); + } + }; + + public static final NumberType LONG = new NumberType() + { + public Object getActualValue(Number number) + { + return number instanceof Long ? number : new Long(number.longValue()); + } + }; + + public static final NumberType DOUBLE = new NumberType() + { + public Object getActualValue(Number number) + { + return number instanceof Double ? number : new Double(number.doubleValue()); + } + }; + + static + { + __numberTypes.put(Short.class, SHORT); + __numberTypes.put(Short.TYPE, SHORT); + __numberTypes.put(Integer.class, INTEGER); + __numberTypes.put(Integer.TYPE, INTEGER); + __numberTypes.put(Long.class, LONG); + __numberTypes.put(Long.TYPE, LONG); + __numberTypes.put(Float.class, FLOAT); + __numberTypes.put(Float.TYPE, FLOAT); + __numberTypes.put(Double.class, DOUBLE); + __numberTypes.put(Double.TYPE, DOUBLE); + } +}