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);
+    }
+}