Package org.exoplatform.ws.frameworks.json.impl

Source Code of org.exoplatform.ws.frameworks.json.impl.BeanBuilder

/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.ws.frameworks.json.impl;

import org.exoplatform.ws.frameworks.json.impl.JsonUtils.Types;
import org.exoplatform.ws.frameworks.json.value.JsonValue;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;

/**
* @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a>
* @version $Id: BeanBuilder.java 34417 2009-07-23 14:42:56Z dkatayev $
*/
public class BeanBuilder
{

   static final Collection<String> SKIP_METHODS = new HashSet<String>();

   static
   {
      // Since we need support for Groovy must skip this.
      // All "Groovy Objects" implements interface groovy.lang.GroovyObject
      // and has method setMetaClass. Not need to process it.
      SKIP_METHODS.add("setMetaClass");
   }

   /**
    * Create Java Bean from Json Source.
    *
    * @param clazz the Class of target Object.
    * @param jsonValue the Json representation.
    * @return Object.
    * @throws Exception if any errors occurs.
    */
   @SuppressWarnings("unchecked")
   public Object createObject(Class<?> clazz, JsonValue jsonValue) throws Exception
   {
      if (JsonUtils.getType(clazz) == Types.ENUM)
      {
         // Enum is not instantiable via CLass.getInstance().
         // This is used when enum is member of array or collection.
         Class c = clazz;
         return Enum.valueOf(c, jsonValue.getStringValue());
      }
      Object object = clazz.newInstance();
      Method[] methods = clazz.getMethods();

      for (Method method : methods)
      {
         String methodName = method.getName();
         Class<?>[] parameterTypes = method.getParameterTypes();

         if (!SKIP_METHODS.contains(methodName) && methodName.startsWith("set") && parameterTypes.length == 1
                  && methodName.length() > 3)
         {
            Class<?> methodParameterClazz = parameterTypes[0];
            String key = methodName.substring(3);
            // first letter to lower case
            key = (key.length() > 1) ? Character.toLowerCase(key.charAt(0)) + key.substring(1) : key.toLowerCase();

            // Bug : WS-53
            if (jsonValue.isNull())
            {
               return null;
            }

            if (!jsonValue.isObject())
            {
               throw new JsonException("Unsupported type of jsonValue for parameter of method " + clazz.getName() + "#"
                  + method.getName());
            }

            JsonValue childJsonValue = jsonValue.getElement(key);

            if (childJsonValue == null)
            {
               continue;
            }

            // if one of known primitive type or array of primitive type
            if (JsonUtils.isKnownType(methodParameterClazz))
            {
               method.invoke(object, new Object[]{createObjectKnownTypes(methodParameterClazz, childJsonValue)});
            }
            else
            {
               Types type = JsonUtils.getType(methodParameterClazz);
               // other type Collection, Map or Object[].
               if (type != null)
               {
                  switch (type)
                  {
                     case ENUM : {
                        Class c = methodParameterClazz;
                        Enum<?> en = Enum.valueOf(c, childJsonValue.getStringValue());
                        method.invoke(object, new Object[]{en});
                     }
                        break;
                     case ARRAY_OBJECT : {
                        Object array = createArray(methodParameterClazz, childJsonValue);
                        method.invoke(object, new Object[]{array});
                     }
                        break;
                     case COLLECTION : {
                        Type[] genericParameterTypes = method.getGenericParameterTypes();
                        Class<?> parameterizedTypeClass = null;
                        if (genericParameterTypes.length == 1)
                        {
                           if (genericParameterTypes[0] instanceof ParameterizedType)
                           {
                              ParameterizedType parameterizedType = (ParameterizedType)genericParameterTypes[0];
                              try
                              {
                                 // Collection can't be parameterized by other Collection,
                                 // Array, etc.
                                 parameterizedTypeClass = (Class<?>)parameterizedType.getActualTypeArguments()[0];
                              }
                              catch (ClassCastException e)
                              {
                                 throw new JsonException("Unsupported parameter in method " + clazz.getName() + "#"
                                    + method.getName()
                                    + ". This type of Collection can't be restored from JSON source.\n"
                                    + "Collection is parameterized by wrong Type: " + parameterizedType + ".");
                              }
                           }
                           else
                           {
                              throw new JsonException("Unsupported parameter in method " + clazz.getName() + "#"
                                 + method.getName() + ". Collection is not parameterized. "
                                 + "Collection<?> is not supported. \nCollection must be parameterized by"
                                 + " any types, or by JavaBean with 'get' and 'set' methods.");
                           }
                        }
                        Constructor<?> constructor = null;
                        if (methodParameterClazz.isInterface()
                           || Modifier.isAbstract(methodParameterClazz.getModifiers()))
                        {
                           try
                           {
                              constructor =
                                 ArrayList.class.asSubclass(methodParameterClazz).getConstructor(
                                    new Class[]{Collection.class});
                           }
                           catch (Exception e)
                           {
                              try
                              {
                                 constructor =
                                    HashSet.class.asSubclass(methodParameterClazz).getConstructor(
                                       new Class[]{Collection.class});
                              }
                              catch (Exception e1)
                              {
                                 try
                                 {
                                    constructor =
                                       LinkedList.class.asSubclass(methodParameterClazz).getConstructor(
                                          new Class[]{Collection.class});
                                 }
                                 catch (Exception e2)
                                 {
                                    // ignore exception here
                                 }
                              }
                           }
                        }
                        else
                        {
                           constructor = methodParameterClazz.getConstructor(new Class[]{Collection.class});
                        }
                        if (constructor == null)
                        {
                           throw new JsonException("Can't find satisfied constructor for : " + methodParameterClazz
                              + ", method : " + clazz.getName() + "#" + method.getName());
                        }

                        ArrayList<Object> sourceCollection = new ArrayList<Object>(childJsonValue.size());

                        Iterator<JsonValue> values = childJsonValue.getElements();

                        while (values.hasNext())
                        {
                           JsonValue v = values.next();
                           if (!JsonUtils.isKnownType(parameterizedTypeClass))
                           {
                              sourceCollection.add(createObject(parameterizedTypeClass, v));
                           }
                           else
                           {
                              sourceCollection.add(createObjectKnownTypes(parameterizedTypeClass, v));
                           }
                        }

                        constructor.newInstance(sourceCollection);
                        method.invoke(object, constructor.newInstance(sourceCollection));
                     }
                        break;
                     case MAP : {
                        Type[] genericParameterTypes = method.getGenericParameterTypes();
                        Class<?> parameterizedTypeClass = null;
                        if (genericParameterTypes.length == 1)
                        {
                           if (genericParameterTypes[0] instanceof ParameterizedType)
                           {
                              ParameterizedType parameterizedType = (ParameterizedType)genericParameterTypes[0];
                              if (!String.class
                                 .isAssignableFrom((Class<?>)parameterizedType.getActualTypeArguments()[0]))
                              {
                                 throw new JsonException("Unsupported parameter in method " + clazz.getName() + "#"
                                    + method.getName() + ". Key of Map must be String.");
                              }
                              try
                              {
                                 parameterizedTypeClass = (Class<?>)parameterizedType.getActualTypeArguments()[1];
                              }
                              catch (Exception e)
                              {
                                 throw new JsonException("Unsupported parameter in method " + clazz.getName() + "#"
                                    + method.getName() + ". This type of Map can't be restored from JSON source.\n"
                                    + "Map is parameterized by wrong Type: " + parameterizedType + ".");
                              }
                           }
                           else
                           {
                              throw new JsonException("Unsupported parameter in method " + clazz.getName() + "#"
                                 + method.getName() + ". Map is not parameterized. "
                                 + "Map<Sting, ?> is not supported. \nMap must be parameterized by"
                                 + "String and any types or JavaBean with 'get' and 'set' methods.");
                           }
                        }
                        Constructor<?> constructor = null;
                        if (methodParameterClazz.isInterface()
                           || Modifier.isAbstract(methodParameterClazz.getModifiers()))
                        {
                           try
                           {
                              constructor =
                                 HashMap.class.asSubclass(methodParameterClazz).getConstructor(new Class[]{Map.class});
                           }
                           catch (Exception e)
                           {
                              try
                              {
                                 constructor =
                                    Hashtable.class.asSubclass(methodParameterClazz).getConstructor(
                                       new Class[]{Map.class});
                              }
                              catch (Exception e1)
                              {
                                 try
                                 {
                                    constructor =
                                       LinkedHashMap.class.asSubclass(methodParameterClazz).getConstructor(
                                          new Class[]{Map.class});
                                 }
                                 catch (Exception e2)
                                 {
                                    // ignore exception here
                                 }
                              }
                           }
                        }
                        else
                        {
                           constructor = methodParameterClazz.getConstructor(new Class[]{Map.class});
                        }

                        if (constructor == null)
                        {
                           throw new JsonException("Can't find satisfied constructor for : " + methodParameterClazz
                              + ", method : " + clazz.getName() + "#" + method.getName());
                        }

                        HashMap<String, Object> sourceMap = new HashMap<String, Object>(childJsonValue.size());

                        Iterator<String> keys = childJsonValue.getKeys();

                        while (keys.hasNext())
                        {
                           String k = keys.next();
                           JsonValue v = childJsonValue.getElement(k);
                           if (!JsonUtils.isKnownType(parameterizedTypeClass))
                           {
                              sourceMap.put(k, createObject(parameterizedTypeClass, v));
                           }
                           else
                           {
                              sourceMap.put(k, createObjectKnownTypes(parameterizedTypeClass, v));
                           }
                        }

                        method.invoke(object, constructor.newInstance(sourceMap));

                     }
                        break;
                     default :
                        // it must never happen!
                        throw new JsonException("Can't restore parameter of method : " + clazz.getName() + "#"
                           + method.getName() + " from JSON source.");
                  }

               }
               else
               {
                  method.invoke(object, createObject(methodParameterClazz, childJsonValue));
               }
            }
         }
      }

      return object;
   }

   /**
    * Create array of Java Object from Json Source include multi-dimension
    * array.
    *
    * @param clazz the Class of target Object.
    * @param jsonValue the Json representation.
    * @return Object.
    * @throws Exception if any errors occurs.
    */
   private Object createArray(Class<?> clazz, JsonValue jsonValue) throws Exception
   {

      Class<?> componentType = clazz.getComponentType();
      Object array = Array.newInstance(componentType, jsonValue.size());
      Iterator<JsonValue> values = jsonValue.getElements();
      int i = 0;

      if (componentType.isArray())
      {
         if (JsonUtils.isKnownType(componentType))
         {
            while (values.hasNext())
            {
               JsonValue v = values.next();
               Array.set(array, i++, createObjectKnownTypes(componentType, v));
            }
         }
         else
         {
            while (values.hasNext())
            {
               JsonValue v = values.next();
               Array.set(array, i++, createArray(componentType, v));
            }
         }
      }
      else
      {
         while (values.hasNext())
         {
            JsonValue v = values.next();
            Array.set(array, i++, createObject(componentType, v));
         }
      }
      return array;
   }

   /**
    * Create Objects of known types.
    *
    * @param clazz class.
    * @param jsonValue JsonValue , @see {@link JsonValue}
    * @return Object.
    * @throws JsonException if type is unknown.
    */
   private Object createObjectKnownTypes(Class<?> clazz, JsonValue jsonValue) throws JsonException
   {
      Types t = JsonUtils.getType(clazz);
      switch (t)
      {
         case NULL :
            return null;
         case BOOLEAN :
            return jsonValue.getBooleanValue();
         case BYTE :
            return jsonValue.getByteValue();
         case SHORT :
            return jsonValue.getShortValue();
         case INT :
            return jsonValue.getIntValue();
         case LONG :
            return jsonValue.getLongValue();
         case FLOAT :
            return jsonValue.getFloatValue();
         case DOUBLE :
            return jsonValue.getDoubleValue();
         case CHAR :
            // TODO check String length
            return jsonValue.getStringValue().charAt(0);
         case STRING :
            return jsonValue.getStringValue();
         case ARRAY_BOOLEAN : {
            boolean[] params = new boolean[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getBooleanValue();
            }
            return params;
         }
         case ARRAY_BYTE : {
            byte[] params = new byte[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getByteValue();
            }
            return params;
         }
         case ARRAY_SHORT : {
            short[] params = new short[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getShortValue();
            }
            return params;
         }
         case ARRAY_INT : {
            int[] params = new int[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getIntValue();
            }
            return params;
         }
         case ARRAY_LONG : {
            long[] params = new long[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getLongValue();
            }
            return params;
         }
         case ARRAY_FLOAT : {
            float[] params = new float[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getFloatValue();
            }
            return params;
         }
         case ARRAY_DOUBLE : {
            double[] params = new double[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getDoubleValue();
            }
            return params;
         }
         case ARRAY_CHAR : {
            char[] params = new char[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            // TODO better checking an transformation string to char
            while (values.hasNext())
            {
               params[i++] = values.next().getStringValue().charAt(0);
            }
            return params;
         }
         case ARRAY_STRING : {
            String[] params = new String[jsonValue.size()];
            Iterator<JsonValue> values = jsonValue.getElements();
            int i = 0;
            while (values.hasNext())
            {
               params[i++] = values.next().getStringValue();
            }
            return params;
         }
         default :
            // Nothing to do for other type. Exception will be thrown.
            break;
      }
      throw new JsonException("Unknown type " + clazz.getName());
   }

}
TOP

Related Classes of org.exoplatform.ws.frameworks.json.impl.BeanBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.