content: /*
   * Copyright 1999-2101 Alibaba Group.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.fastjson.util;
  
  import java.lang.reflect.Array;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Proxy;
  import java.lang.reflect.Type;
  import java.lang.reflect.TypeVariable;
  import java.lang.reflect.WildcardType;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Date;
  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.List;
  import java.util.Map;
  import java.util.TreeMap;
  import java.util.TreeSet;
  import java.util.Vector;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentMap;
  
  import com.alibaba.fastjson.JSON;
  import com.alibaba.fastjson.JSONArray;
  import com.alibaba.fastjson.JSONException;
  import com.alibaba.fastjson.JSONObject;
  import com.alibaba.fastjson.annotation.JSONField;
  import com.alibaba.fastjson.annotation.JSONType;
  import com.alibaba.fastjson.parser.ParserConfig;
  import com.alibaba.fastjson.parser.deserializer.FieldDeserializer;
  
  /**
   * @author wenshao<szujobs@hotmail.com>
   */
  public class TypeUtils {
  
      public static final String castToString(Object value) {
          if (value == null) {
              return null;
          }
  
          return value.toString();
      }
  
      public static final Byte castToByte(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Number) {
              return ((Number) value).byteValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
              return Byte.parseByte(strVal);
          }
  
          throw new JSONException("can not cast to byte, value : " + value);
      }
  
      public static final Character castToChar(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Character) {
              return (Character) value;
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
  
              if (strVal.length() == 0) {
                  return null;
              }
  
              if (strVal.length() != 1) {
                  throw new JSONException("can not cast to byte, value : " + value);
              }
  
              return strVal.charAt(0);
          }
  
          throw new JSONException("can not cast to byte, value : " + value);
      }
  
      public static final Short castToShort(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Number) {
              return ((Number) value).shortValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
              return Short.parseShort(strVal);
          }
  
          throw new JSONException("can not cast to short, value : " + value);
      }
  
      public static final BigDecimal castToBigDecimal(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof BigDecimal) {
              return (BigDecimal) value;
          }
  
          if (value instanceof BigInteger) {
              return new BigDecimal((BigInteger) value);
          }
  
          String strVal = value.toString();
          if (strVal.length() == 0) {
              return null;
          }
  
          return new BigDecimal(strVal);
      }
  
      public static final BigInteger castToBigInteger(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof BigInteger) {
              return (BigInteger) value;
          }
  
          if (value instanceof Float || value instanceof Double) {
              return BigInteger.valueOf(((Number) value).longValue());
          }
  
          String strVal = value.toString();
          if (strVal.length() == 0) {
              return null;
          }
  
          return new BigInteger(strVal);
      }
  
      public static final Float castToFloat(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Number) {
              return ((Number) value).floatValue();
          }
  
          if (value instanceof String) {
              String strVal = value.toString();
              if (strVal.length() == 0) {
                  return null;
              }
  
              return Float.parseFloat(strVal);
          }
  
          throw new JSONException("can not cast to float, value : " + value);
      }
  
      public static final Double castToDouble(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Number) {
              return ((Number) value).doubleValue();
          }
  
          if (value instanceof String) {
              String strVal = value.toString();
              if (strVal.length() == 0) {
                  return null;
              }
              return Double.parseDouble(strVal);
          }
  
          throw new JSONException("can not cast to double, value : " + value);
      }
  
      public static final Date castToDate(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Calendar) {
              return ((Calendar) value).getTime();
          }
  
          if (value instanceof Date) {
              return (Date) value;
          }
  
          long longValue = 0;
  
          if (value instanceof Number) {
              longValue = ((Number) value).longValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
  
              if (strVal.indexOf('-') != -1) {
                  String format;
                  if (strVal.length() == JSON.DEFFAULT_DATE_FORMAT.length()) {
                      format = JSON.DEFFAULT_DATE_FORMAT;
                  } else if (strVal.length() == 10) {
                      format = "yyyy-MM-dd";
                  } else if (strVal.length() == "yyyy-MM-dd HH:mm:ss".length()) {
                      format = "yyyy-MM-dd HH:mm:ss";
                  } else {
                      format = "yyyy-MM-dd HH:mm:ss.SSS";
                  }
  
                  SimpleDateFormat dateFormat = new SimpleDateFormat(format);
                  try {
                      return (Date) dateFormat.parse(strVal);
                  } catch (ParseException e) {
                      throw new JSONException("can not cast to Date, value : " + strVal);
                  }
              }
  
              if (strVal.length() == 0) {
                  return null;
              }
  
              longValue = Long.parseLong(strVal);
          }
  
          if (longValue <= 0) {
              throw new JSONException("can not cast to Date, value : " + value);
          }
  
          return new Date(longValue);
      }
  
      public static final java.sql.Date castToSqlDate(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Calendar) {
              return new java.sql.Date(((Calendar) value).getTimeInMillis());
          }
  
          if (value instanceof java.sql.Date) {
              return (java.sql.Date) value;
          }
  
          if (value instanceof java.util.Date) {
              return new java.sql.Date(((java.util.Date) value).getTime());
          }
  
          long longValue = 0;
  
          if (value instanceof Number) {
              longValue = ((Number) value).longValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
  
              longValue = Long.parseLong(strVal);
          }
  
          if (longValue <= 0) {
              throw new JSONException("can not cast to Date, value : " + value);
          }
  
          return new java.sql.Date(longValue);
      }
  
      public static final java.sql.Timestamp castToTimestamp(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Calendar) {
              return new java.sql.Timestamp(((Calendar) value).getTimeInMillis());
          }
  
          if (value instanceof java.sql.Timestamp) {
              return (java.sql.Timestamp) value;
          }
  
          if (value instanceof java.util.Date) {
              return new java.sql.Timestamp(((java.util.Date) value).getTime());
          }
  
          long longValue = 0;
  
          if (value instanceof Number) {
              longValue = ((Number) value).longValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
  
              longValue = Long.parseLong(strVal);
          }
  
          if (longValue <= 0) {
              throw new JSONException("can not cast to Date, value : " + value);
          }
  
          return new java.sql.Timestamp(longValue);
      }
  
      public static final Long castToLong(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Number) {
              return ((Number) value).longValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
  
              return Long.parseLong(strVal);
          }
  
          throw new JSONException("can not cast to long, value : " + value);
      }
  
      public static final Integer castToInt(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Integer) {
              return (Integer) value;
          }
  
          if (value instanceof Number) {
              return ((Number) value).intValue();
          }
  
          if (value instanceof String) {
              String strVal = (String) value;
              if (strVal.length() == 0) {
                  return null;
              }
  
              return Integer.parseInt(strVal);
          }
  
          throw new JSONException("can not cast to int, value : " + value);
      }
  
      public static final byte[] castToBytes(Object value) {
          if (value instanceof byte[]) {
              return (byte[]) value;
          }
  
          if (value instanceof String) {
              return Base64.decodeFast((String) value);
          }
          throw new JSONException("can not cast to int, value : " + value);
      }
  
      public static final Boolean castToBoolean(Object value) {
          if (value == null) {
              return null;
          }
  
          if (value instanceof Boolean) {
              return (Boolean) value;
          }
  
          if (value instanceof Number) {
              return ((Number) value).intValue() == 1;
          }
  
          if (value instanceof String) {
              String str = (String) value;
              if (str.length() == 0) {
                  return null;
              }
  
              if ("true".equals(str)) {
                  return Boolean.TRUE;
              }
              if ("false".equals(str)) {
                  return Boolean.FALSE;
              }
  
              if ("1".equals(str)) {
                  return Boolean.TRUE;
              }
          }
  
          throw new JSONException("can not cast to int, value : " + value);
      }
  
      public static final <T> T castToJavaBean(Object obj, Class<T> clazz) {
          return cast(obj, clazz, ParserConfig.getGlobalInstance());
      }
  
      @SuppressWarnings({ "unchecked", "rawtypes" })
      public static final <T> T cast(Object obj, Class<T> clazz, ParserConfig mapping) {
          if (obj == null) {
              return null;
          }
          
          if (clazz == null) {
          	throw new IllegalArgumentException("clazz is null");
          }
  
          if (clazz == obj.getClass()) {
              return (T) obj;
          }
  
          if (obj instanceof Map) {
              if (clazz == Map.class) {
                  return (T) obj;
              }
  
              return castToJavaBean((Map<String, Object>) obj, clazz, mapping);
          }
  
          if (clazz.isArray()) {
              if (obj instanceof Collection) {
  
                  Collection collection = (Collection) obj;
                  int index = 0;
                  Object array = Array.newInstance(clazz.getComponentType(), collection.size());
                  for (Object item : collection) {
                      Object value = cast(item, clazz.getComponentType(), mapping);
                      Array.set(array, index, value);
                      index++;
                  }
  
                  return (T) array;
              }
          }
  
          if (clazz.isAssignableFrom(obj.getClass())) {
              return (T) obj;
          }
  
          if (clazz == boolean.class || clazz == Boolean.class) {
              return (T) castToBoolean(obj);
          }
  
          if (clazz == byte.class || clazz == Byte.class) {
              return (T) castToByte(obj);
          }
  
          // if (clazz == char.class || clazz == Character.class) {
          // return (T) castToCharacter(obj);
          // }
  
          if (clazz == short.class || clazz == Short.class) {
              return (T) castToShort(obj);
          }
  
          if (clazz == int.class || clazz == Integer.class) {
              return (T) castToInt(obj);
          }
  
          if (clazz == long.class || clazz == Long.class) {
              return (T) castToLong(obj);
          }
  
          if (clazz == float.class || clazz == Float.class) {
              return (T) castToFloat(obj);
          }
  
          if (clazz == double.class || clazz == Double.class) {
              return (T) castToDouble(obj);
          }
  
          if (clazz == String.class) {
              return (T) castToString(obj);
          }
  
          if (clazz == BigDecimal.class) {
              return (T) castToBigDecimal(obj);
          }
  
          if (clazz == BigInteger.class) {
              return (T) castToBigInteger(obj);
          }
  
          if (clazz == Date.class) {
              return (T) castToDate(obj);
          }
  
          if (clazz == java.sql.Date.class) {
              return (T) castToSqlDate(obj);
          }
  
          if (clazz == java.sql.Timestamp.class) {
              return (T) castToTimestamp(obj);
          }
  
          if (clazz.isEnum()) {
              return (T) castToEnum(obj, clazz, mapping);
          }
  
          if (Calendar.class.isAssignableFrom(clazz)) {
              Date date = castToDate(obj);
              Calendar calendar;
              if (clazz == Calendar.class) {
                  calendar = Calendar.getInstance();
              } else {
                  try {
                      calendar = (Calendar) clazz.newInstance();
                  } catch (Exception e) {
                      throw new JSONException("can not cast to : " + clazz.getName(), e);
                  }
              }
              calendar.setTime(date);
              return (T) calendar;
          }
  
          if (obj instanceof String) {
              String strVal = (String) obj;
              if (strVal.length() == 0) {
                  return null;
              }
          }
  
          throw new JSONException("can not cast to : " + clazz.getName());
      }
  
      @SuppressWarnings({ "unchecked", "rawtypes" })
      public static final <T> T castToEnum(Object obj, Class<T> clazz, ParserConfig mapping) {
          try {
              if (obj instanceof String) {
                  String name = (String) obj;
                  if (name.length() == 0) {
                      return null;
                  }
  
                  return (T) Enum.valueOf((Class<? extends Enum>) clazz, name);
              }
  
              if (obj instanceof Number) {
                  int ordinal = ((Number) obj).intValue();
  
                  Method method = clazz.getMethod("values");
                  Object[] values = (Object[]) method.invoke(null);
                  for (Object value : values) {
                      Enum e = (Enum) value;
                      if (e.ordinal() == ordinal) {
                          return (T) e;
                      }
                  }
              }
          } catch (Exception ex) {
              throw new JSONException("can not cast to : " + clazz.getName(), ex);
          }
  
          throw new JSONException("can not cast to : " + clazz.getName());
      }
  
      @SuppressWarnings("unchecked")
      public static final <T> T cast(Object obj, Type type, ParserConfig mapping) {
          if (obj == null) {
              return null;
          }
  
          if (type instanceof Class) {
              return (T) cast(obj, (Class<T>) type, mapping);
          }
  
          if (type instanceof ParameterizedType) {
              return (T) cast(obj, (ParameterizedType) type, mapping);
          }
  
          if (obj instanceof String) {
              String strVal = (String) obj;
              if (strVal.length() == 0) {
                  return null;
              }
          }
  
          if (type instanceof TypeVariable) {
              return (T) obj;
          }
  
          throw new JSONException("can not cast to : " + type);
      }
  
      @SuppressWarnings({ "rawtypes", "unchecked" })
      public static final <T> T cast(Object obj, ParameterizedType type, ParserConfig mapping) {
          Type rawTye = type.getRawType();
  
          if (rawTye == List.class || rawTye == ArrayList.class) {
              Type itemType = type.getActualTypeArguments()[0];
  
              if (obj instanceof Iterable) {
                  List list = new ArrayList();
  
                  for (Iterator it = ((Iterable) obj).iterator(); it.hasNext();) {
                      Object item = it.next();
                      list.add(cast(item, itemType, mapping));
                  }
  
                  return (T) list;
              }
          }
  
          if (rawTye == Map.class || rawTye == HashMap.class) {
              Type keyType = type.getActualTypeArguments()[0];
              Type valueType = type.getActualTypeArguments()[1];
  
              if (obj instanceof Map) {
                  Map map = new HashMap();
  
                  for (Map.Entry entry : ((Map<?, ?>) obj).entrySet()) {
                      Object key = cast(entry.getKey(), keyType, mapping);
                      Object value = cast(entry.getValue(), valueType, mapping);
  
                      map.put(key, value);
                  }
  
                  return (T) map;
              }
          }
  
          if (obj instanceof String) {
              String strVal = (String) obj;
              if (strVal.length() == 0) {
                  return null;
              }
          }
  
          if (type.getActualTypeArguments().length == 1) {
              Type argType = type.getActualTypeArguments()[0];
              if (argType instanceof WildcardType) {
                  return (T) cast(obj, rawTye, mapping);
              }
          }
  
          throw new JSONException("can not cast to : " + type);
      }
  
      @SuppressWarnings({ "unchecked" })
      public static final <T> T castToJavaBean(Map<String, Object> map, Class<T> clazz, ParserConfig mapping) {
          try {
              if (clazz == StackTraceElement.class) {
                  String declaringClass = (String) map.get("className");
                  String methodName = (String) map.get("methodName");
                  String fileName = (String) map.get("fileName");
                  int lineNumber;
                  {
                      Number value = (Number) map.get("lineNumber");
                      if (value == null) {
                          lineNumber = 0;
                      } else {
                          lineNumber = value.intValue();
                      }
                  }
  
                  return (T) new StackTraceElement(declaringClass, methodName, fileName, lineNumber);
              }
  
              {
                  Object iClassObject = map.get("@type");
                  if (iClassObject instanceof String) {
                      String className = (String) iClassObject;
  
                      clazz = (Class<T>) loadClass(className);
                      
                      if (clazz == null) {
                      	throw new ClassNotFoundException(className + " not found");
                      }
                  }
              }
  
              if (clazz.isInterface()) {
                  JSONObject object;
  
                  if (map instanceof JSONObject) {
                      object = (JSONObject) map;
                  } else {
                      object = new JSONObject(map);
                  }
  
                  return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                                                    new Class<?>[] { clazz }, object);
              }
  
              Map<String, FieldDeserializer> setters = mapping.getFieldDeserializers(clazz);
  
              T object = clazz.newInstance();
  
              for (Map.Entry<String, FieldDeserializer> entry : setters.entrySet()) {
                  String key = entry.getKey();
                  Method method = entry.getValue().getMethod();
  
                  if (map.containsKey(key)) {
                      Object value = map.get(key);
                      value = cast(value, method.getGenericParameterTypes()[0], mapping);
                      method.invoke(object, new Object[] { value });
                  }
              }
  
              return object;
          } catch (Exception e) {
              throw new JSONException(e.getMessage(), e);
          }
      }
  
      private static ConcurrentMap<String, Class<?>> mappings = new ConcurrentHashMap<String, Class<?>>();
      static {
          addBaseClassMappings();
      }
  
      public static void addClassMapping(String className, Class<?> clazz) {
          if (className == null) {
              className = clazz.getName();
          }
  
          mappings.put(className, clazz);
      }
      
      public static void addBaseClassMappings() {
          mappings.put("byte", byte.class);
          mappings.put("short", short.class);
          mappings.put("int", int.class);
          mappings.put("long", long.class);
          mappings.put("float", float.class);
          mappings.put("double", double.class);
          mappings.put("boolean", boolean.class);
          mappings.put("char", char.class);
  
          mappings.put("[byte", byte[].class);
          mappings.put("[short", short[].class);
          mappings.put("[int", int[].class);
          mappings.put("[long", long[].class);
          mappings.put("[float", float[].class);
          mappings.put("[double", double[].class);
          mappings.put("[boolean", boolean[].class);
          mappings.put("[char", char[].class);
          
          mappings.put(HashMap.class.getName(), HashMap.class);
      }
      
      public static void clearClassMapping() {
          mappings.clear();
          addBaseClassMappings();
      }
  
      public static Class<?> loadClass(String className) {
          if (className == null || className.length() == 0) {
              return null;
          }
  
          Class<?> clazz = mappings.get(className);
  
          if (clazz != null) {
              return clazz;
          }
  
          if (className.charAt(0) == '[') {
              Class<?> componentType = loadClass(className.substring(1));
              return Array.newInstance(componentType, 0).getClass();
          }
  
          if (className.startsWith("L") && className.endsWith(";")) {
              String newClassName = className.substring(1, className.length() - 1);
              return loadClass(newClassName);
          }
  
          try {
              clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
  
              addClassMapping(className, clazz);
  
              return clazz;
          } catch (Throwable e) {
              // skip
          }
  
          try {
              clazz = Class.forName(className);
  
              addClassMapping(className, clazz);
  
              return clazz;
          } catch (Throwable e) {
              // skip
          }
  
          return clazz;
      }
  
      public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap) {
          return computeGetters(clazz, aliasMap, true);
      }
  
      public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap, boolean sorted) {
          Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();
  
          for (Method method : clazz.getMethods()) {
              String methodName = method.getName();
  
              if (Modifier.isStatic(method.getModifiers())) {
                  continue;
              }
  
              if (method.getReturnType().equals(Void.TYPE)) {
                  continue;
              }
  
              if (method.getParameterTypes().length != 0) {
                  continue;
              }
  
              if (method.getReturnType() == ClassLoader.class) {
                  continue;
              }
  
              if (method.getName().equals("getMetaClass")
                  && method.getReturnType().getName().equals("groovy.lang.MetaClass")) {
                  continue;
              }
  
              JSONField annotation = method.getAnnotation(JSONField.class);
  
              if (annotation != null) {
                  if (!annotation.serialize()) {
                      continue;
                  }
  
                  if (annotation.name().length() != 0) {
                      String propertyName = annotation.name();
  
                      if (aliasMap != null) {
                          propertyName = aliasMap.get(propertyName);
                          if (propertyName == null) {
                              continue;
                          }
                      }
  
                      fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, null));
                      continue;
                  }
              }
  
              if (methodName.startsWith("get")) {
                  if (methodName.length() < 4) {
                      continue;
                  }
  
                  if (methodName.equals("getClass")) {
                      continue;
                  }
  
                  if (!Character.isUpperCase(methodName.charAt(3))) {
                      continue;
                  }
  
                  String propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
  
                  boolean ignore = isJSONTypeIgnore(clazz, propertyName);
  
                  if (ignore) {
                      continue;
                  }
  
                  Field field = ParserConfig.getField(clazz, propertyName);
                  if (field == null) {
                  	field = ParserConfig.getField(clazz, methodName.substring(3));
                  }
                  
                  if (field != null) {
                      JSONField fieldAnnotation = field.getAnnotation(JSONField.class);
  
                      if (fieldAnnotation != null && fieldAnnotation.name().length() != 0) {
                          propertyName = fieldAnnotation.name();
  
                          if (aliasMap != null) {
                              propertyName = aliasMap.get(propertyName);
                              if (propertyName == null) {
                                  continue;
                              }
                          }
                      }
                  }
  
                  if (aliasMap != null) {
                      propertyName = aliasMap.get(propertyName);
                      if (propertyName == null) {
                          continue;
                      }
                  }
  
                  fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field));
              }
  
              if (methodName.startsWith("is")) {
                  if (methodName.length() < 3) {
                      continue;
                  }
  
                  if (!Character.isUpperCase(methodName.charAt(2))) {
                      continue;
                  }
  
                  String propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
  
                  Field field = ParserConfig.getField(clazz, propertyName);
                  if (field != null) {
                      JSONField fieldAnnotation = field.getAnnotation(JSONField.class);
  
                      if (fieldAnnotation != null && fieldAnnotation.name().length() != 0) {
                          propertyName = fieldAnnotation.name();
  
                          if (aliasMap != null) {
                              propertyName = aliasMap.get(propertyName);
                              if (propertyName == null) {
                                  continue;
                              }
                          }
                      }
                  }
  
                  if (aliasMap != null) {
                      propertyName = aliasMap.get(propertyName);
                      if (propertyName == null) {
                          continue;
                      }
                  }
  
                  fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field));
              }
          }
  
          for (Field field : clazz.getFields()) {
              if (Modifier.isStatic(field.getModifiers())) {
                  continue;
              }
  
              if (!Modifier.isPublic(field.getModifiers())) {
                  continue;
              }
  
              if (!fieldInfoMap.containsKey(field.getName())) {
                  fieldInfoMap.put(field.getName(), new FieldInfo(field.getName(), null, field));
              }
          }
  
          List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();
  
          boolean containsAll = false;
          String[] orders = null;
  
          JSONType annotation = clazz.getAnnotation(JSONType.class);
          if (annotation != null) {
              orders = annotation.orders();
  
              if (orders != null && orders.length == fieldInfoMap.size()) {
                  containsAll = true;
                  for (String item : orders) {
                      if (!fieldInfoMap.containsKey(item)) {
                          containsAll = false;
                          break;
                      }
                  }
              } else {
                  containsAll = false;
              }
          }
  
          if (containsAll) {
              for (String item : orders) {
                  FieldInfo fieldInfo = fieldInfoMap.get(item);
                  fieldInfoList.add(fieldInfo);
              }
          } else {
              for (FieldInfo fieldInfo : fieldInfoMap.values()) {
                  fieldInfoList.add(fieldInfo);
              }
  
              if (sorted) {
                  Collections.sort(fieldInfoList);
              }
          }
  
          return fieldInfoList;
      }
  
      private static boolean isJSONTypeIgnore(Class<?> clazz, String propertyName) {
          JSONType jsonType = clazz.getAnnotation(JSONType.class);
  
          if (jsonType != null && jsonType.ignores() != null) {
              for (String item : jsonType.ignores()) {
                  if (propertyName.equalsIgnoreCase(item)) {
                      return true;
                  }
              }
          }
  
          if (clazz.getSuperclass() != Object.class && clazz.getSuperclass() != null) {
              if (isJSONTypeIgnore(clazz.getSuperclass(), propertyName)) {
                  return true;
              }
          }
  
          return false;
      }
  
      public static Class<?> getClass(Type type) {
          if (type.getClass() == Class.class) {
              return (Class<?>) type;
          }
  
          if (type instanceof ParameterizedType) {
              return getClass(((ParameterizedType) type).getRawType());
          }
  
          return Object.class;
      }
      
      @SuppressWarnings({ "rawtypes", "unchecked" })
  	public static Object clone(Object o) {
      	if (o == null) {
      		return null;
      	}
      	
      	if (o instanceof Map) {
      		Map map = (Map) o;
      		Class<?> mapClass = map.getClass();
      		
      		Map newMap;
      		if (mapClass == HashMap.class) {
      			newMap = new HashMap();
      		} else if (mapClass == JSONObject.class) {
      			newMap = new JSONObject();
      		} else if (mapClass == LinkedHashMap.class) {
      			newMap = new LinkedHashMap();
      		} else if (mapClass == TreeMap.class) {
      			newMap = new TreeMap();
      		} else if (mapClass == AntiCollisionHashMap.class) {
      			newMap = new AntiCollisionHashMap();
      		} else if (mapClass == ConcurrentHashMap.class) {
      			newMap = new ConcurrentHashMap();
      		} else if (mapClass == Hashtable.class) {
      			newMap = new Hashtable();
        		} else if (map == Collections.EMPTY_MAP) {
        			newMap = map;
      		} else {
      			throw new UnsupportedOperationException("type not suppport : " + mapClass);
      		}
      		
      		for (Object item : map.entrySet()) {
      			Map.Entry entry = (Map.Entry) item;
      			Object key = clone(entry.getKey());
      			Object value = clone(entry.getValue());
      			
      			newMap.put(key, value);
      		}
      		
      		return newMap;
      	}
      	
      	if (o instanceof Collection) {
      		Collection collection = (Collection) o;
      		Class<?> collectionClass = collection.getClass();
      		
      		Collection newCollection;
      		if (collectionClass == ArrayList.class) {
      			newCollection = new ArrayList();
      		} else if (collectionClass == JSONArray.class) {
      			newCollection = new JSONArray();
         		} else if (collectionClass == LinkedList.class) {
      			newCollection = new LinkedList();
         		} else if (collectionClass == Vector.class) {
      			newCollection = new Vector();
         		} else if (collectionClass == HashSet.class) {
      			newCollection = new HashSet();
         		} else if (collectionClass == TreeSet.class) {
      			newCollection = new TreeSet();
        		} else if (collection == Collections.EMPTY_SET //
        				|| collection == Collections.EMPTY_LIST) {
      			newCollection = collection;
      		} else {
      			throw new UnsupportedOperationException("type not suppport : " + collectionClass);
      		}
      		
      		for (Object item : collection) {
      			newCollection.add(item);
      		}
      		
      		return newCollection;
      	}
      	
      	return o;
      }
  }
  