org.eclipse.jetty.util.ajax.JSONPojoConvertor Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2015 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, NumberType> __numberTypes = new HashMap, NumberType>();
public static NumberType getNumberType(Class> clazz)
{
return __numberTypes.get(clazz);
}
protected boolean _fromJSON;
protected Class> _pojoClass;
protected Map _getters = new HashMap();
protected Map _setters = new HashMap();
protected Set _excluded;
/**
* @param pojoClass The class to convert
*/
public JSONPojoConvertor(Class> pojoClass)
{
this(pojoClass, (Set)null, true);
}
/**
* @param pojoClass The class to convert
* @param excluded The fields to exclude
*/
public JSONPojoConvertor(Class> pojoClass, String[] excluded)
{
this(pojoClass, new HashSet(Arrays.asList(excluded)), true);
}
/**
* @param pojoClass The class to convert
* @param excluded The fields to exclude
*/
public JSONPojoConvertor(Class> pojoClass, Set 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 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)null, fromJSON);
}
/* ------------------------------------------------------------ */
protected void init()
{
Method[] methods = _pojoClass.getMethods();
for (int i=0;i2)
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 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
© 2015 - 2024 Weber Informatics LLC | Privacy Policy