content: package com.alibaba.fastjson.util;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.Type;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  
  import com.alibaba.fastjson.JSONException;
  import com.alibaba.fastjson.annotation.JSONCreator;
  import com.alibaba.fastjson.annotation.JSONField;
  
  public class DeserializeBeanInfo {
  
      private final Class<?>        clazz;
      private final Type            type;
      private Constructor<?>        defaultConstructor;
      private Constructor<?>        creatorConstructor;
      private Method                factoryMethod;
  
      private final List<FieldInfo> fieldList = new ArrayList<FieldInfo>();
  
      public DeserializeBeanInfo(Class<?> clazz){
          super();
          this.clazz = clazz;
          this.type = clazz;
      }
  
      public Constructor<?> getDefaultConstructor() {
          return defaultConstructor;
      }
  
      public void setDefaultConstructor(Constructor<?> defaultConstructor) {
          this.defaultConstructor = defaultConstructor;
      }
  
      public Constructor<?> getCreatorConstructor() {
          return creatorConstructor;
      }
  
      public void setCreatorConstructor(Constructor<?> createConstructor) {
          this.creatorConstructor = createConstructor;
      }
  
      public Method getFactoryMethod() {
          return factoryMethod;
      }
  
      public void setFactoryMethod(Method factoryMethod) {
          this.factoryMethod = factoryMethod;
      }
  
      public Class<?> getClazz() {
          return clazz;
      }
  
      public Type getType() {
          return type;
      }
  
      public List<FieldInfo> getFieldList() {
          return fieldList;
      }
      
      public FieldInfo getField(String propertyName) {
          for (FieldInfo item : this.fieldList) {
              if (item.getName().equals(propertyName)) {
                  return item;
              }
          }
          
          return null;
      }
      
      public boolean add(FieldInfo field) {
          for (FieldInfo item : this.fieldList) {
              if (item.getName().equals(field.getName())) {
                  return false;
              }
          }
          fieldList.add(field);
          
          return true;
      }
      
      public static DeserializeBeanInfo computeSetters(Class<?> clazz) {
          return computeSetters(clazz, clazz);
      }
  
      public static DeserializeBeanInfo computeSetters(Class<?> clazz, Type type) {
          DeserializeBeanInfo beanInfo = new DeserializeBeanInfo(clazz);
  
          Constructor<?> defaultConstructor = getDefaultConstructor(clazz);
          if (defaultConstructor != null) {
              defaultConstructor.setAccessible(true);
              beanInfo.setDefaultConstructor(defaultConstructor);
          } else if (defaultConstructor == null && !(clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()))) {
              Constructor<?> creatorConstructor = getCreatorConstructor(clazz);
              if (creatorConstructor != null) {
                  creatorConstructor.setAccessible(true);
                  beanInfo.setCreatorConstructor(creatorConstructor);
  
                  for (int i = 0; i < creatorConstructor.getParameterTypes().length; ++i) {
                      Annotation[] paramAnnotations = creatorConstructor.getParameterAnnotations()[i];
                      JSONField fieldAnnotation = null;
                      for (Annotation paramAnnotation : paramAnnotations) {
                          if (paramAnnotation instanceof JSONField) {
                              fieldAnnotation = (JSONField) paramAnnotation;
                              break;
                          }
                      }
                      if (fieldAnnotation == null) {
                          throw new JSONException("illegal json creator");
                      }
  
                      Class<?> fieldClass = creatorConstructor.getParameterTypes()[i];
                      Type fieldType = creatorConstructor.getGenericParameterTypes()[i];
                      Field field = getField(clazz, fieldAnnotation.name());
                      if (field != null) {
                          field.setAccessible(true);
                      }
                      FieldInfo fieldInfo = new FieldInfo(fieldAnnotation.name(), clazz, fieldClass, fieldType, null,
                                                          field);
                      beanInfo.add(fieldInfo);
                  }
                  return beanInfo;
              }
  
              Method factoryMethod = getFactoryMethod(clazz);
              if (factoryMethod != null) {
                  factoryMethod.setAccessible(true);
                  beanInfo.setFactoryMethod(factoryMethod);
  
                  for (int i = 0; i < factoryMethod.getParameterTypes().length; ++i) {
                      Annotation[] paramAnnotations = factoryMethod.getParameterAnnotations()[i];
                      JSONField fieldAnnotation = null;
                      for (Annotation paramAnnotation : paramAnnotations) {
                          if (paramAnnotation instanceof JSONField) {
                              fieldAnnotation = (JSONField) paramAnnotation;
                              break;
                          }
                      }
                      if (fieldAnnotation == null) {
                          throw new JSONException("illegal json creator");
                      }
  
                      Class<?> fieldClass = factoryMethod.getParameterTypes()[i];
                      Type fieldType = factoryMethod.getGenericParameterTypes()[i];
                      Field field = getField(clazz, fieldAnnotation.name());
                      if (field != null) {
                          field.setAccessible(true);
                      }
                      FieldInfo fieldInfo = new FieldInfo(fieldAnnotation.name(), clazz, fieldClass, fieldType, null,
                                                          field);
                      beanInfo.add(fieldInfo);
                  }
                  return beanInfo;
              }
              
              throw new JSONException("default constructor not found. " + clazz);
          }
  
          for (Method method : clazz.getMethods()) {
              String methodName = method.getName();
              if (methodName.length() < 4) {
                  continue;
              }
  
              if (Modifier.isStatic(method.getModifiers())) {
                  continue;
              }
  
              // support builder set
              if (!(method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(clazz))) {
                  continue;
              }
  
              if (method.getParameterTypes().length != 1) {
                  continue;
              }
  
              JSONField annotation = method.getAnnotation(JSONField.class);
  
              if (annotation != null) {
                  if (!annotation.deserialize()) {
                      continue;
                  }
  
                  if (annotation.name().length() != 0) {
                      String propertyName = annotation.name();
                      beanInfo.add(new FieldInfo(propertyName, method, null, clazz, type));
                      method.setAccessible(true);
                      continue;
                  }
              }
  
              if (methodName.startsWith("set") && Character.isUpperCase(methodName.charAt(3))) {
                  String propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
  
                  Field field = getField(clazz, propertyName);
                  if (field != null) {
  
                      JSONField fieldAnnotation = field.getAnnotation(JSONField.class);
  
                      if (fieldAnnotation != null && fieldAnnotation.name().length() != 0) {
                          propertyName = fieldAnnotation.name();
  
                          beanInfo.add(new FieldInfo(propertyName, method, field, clazz, type));
                          continue;
                      }
                  }
  
                  beanInfo.add(new FieldInfo(propertyName, method, null, clazz, type));
                  method.setAccessible(true);
              }
          }
  
          for (Field field : clazz.getFields()) {
              if (Modifier.isStatic(field.getModifiers())) {
                  continue;
              }
  
              if (!Modifier.isPublic(field.getModifiers())) {
                  continue;
              }
              
              boolean contains = false;
              for (FieldInfo item : beanInfo.getFieldList()) {
                  if (item.getName().equals(field.getName())) {
                      contains = true;
                      continue;
                  }
              }
              
              if (contains) {
                  continue;
              }
  
              beanInfo.add(new FieldInfo(field.getName(), null, field));
          }
          
          for (Method method : clazz.getMethods()) {
              String methodName = method.getName();
              if (methodName.length() < 4) {
                  continue;
              }
  
              if (Modifier.isStatic(method.getModifiers())) {
                  continue;
              }
              
              if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) {
                  if (method.getParameterTypes().length != 0) {
                      continue;
                  }
                  
                  if (!Collection.class.isAssignableFrom(method.getReturnType())) {
                      continue;
                  }
                  
                  String propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
                  
                  FieldInfo fieldInfo = beanInfo.getField(propertyName);
                  if (fieldInfo != null) {
                      continue;
                  }
                  
                  beanInfo.add(new FieldInfo(propertyName, method, null, clazz, type));
                  method.setAccessible(true);
              }
          }
  
          return beanInfo;
      }
  
      public static Field getField(Class<?> clazz, String fieldName) {
          try {
              return clazz.getDeclaredField(fieldName);
          } catch (Exception e) {
              return null;
          }
      }
  
      public static Constructor<?> getDefaultConstructor(Class<?> clazz) {
          if (Modifier.isAbstract(clazz.getModifiers())) {
              return null;
          }
  
          Constructor<?> defaultConstructor = null;
          for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
              if (constructor.getParameterTypes().length == 0) {
                  defaultConstructor = constructor;
                  break;
              }
          }
          
          if (defaultConstructor == null) {
              if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
                  for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
                      if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0].equals(clazz.getDeclaringClass())) {
                          defaultConstructor = constructor;
                          break;
                      }
                  }
              }
          }
          
          return defaultConstructor;
      }
  
      public static Constructor<?> getCreatorConstructor(Class<?> clazz) {
          Constructor<?> creatorConstructor = null;
  
          for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
              JSONCreator annotation = constructor.getAnnotation(JSONCreator.class);
              if (annotation != null) {
                  if (creatorConstructor != null) {
                      throw new JSONException("multi-json creator");
                  }
  
                  creatorConstructor = constructor;
                  break;
              }
          }
          return creatorConstructor;
      }
  
      public static Method getFactoryMethod(Class<?> clazz) {
          Method factoryMethod = null;
  
          for (Method method : clazz.getDeclaredMethods()) {
              if (!Modifier.isStatic(method.getModifiers())) {
                  continue;
              }
  
              if (!clazz.isAssignableFrom(method.getReturnType())) {
                  continue;
              }
  
              JSONCreator annotation = method.getAnnotation(JSONCreator.class);
              if (annotation != null) {
                  if (factoryMethod != null) {
                      throw new JSONException("multi-json creator");
                  }
  
                  factoryMethod = method;
                  break;
              }
          }
          return factoryMethod;
      }
  
  }
  