001package gudusoft.gsqlparser.dlineage.util; 002 003import gudusoft.gsqlparser.util.json.JSONException; 004import gudusoft.gsqlparser.util.json.JSONField; 005 006import java.beans.IntrospectionException; 007import java.beans.Introspector; 008import java.beans.PropertyDescriptor; 009import java.lang.reflect.Field; 010import java.util.*; 011 012@SuppressWarnings("rawtypes") 013public abstract class BeanUtil { 014 015 private static final Map<String, PropertyDescriptor[]> cacheDescriptors = new WeakHashMap<>(); 016 017 @SuppressWarnings("unchecked") 018 public static LinkedHashMap<String, Object> bean2Map(Object src) { 019 if (src instanceof LinkedHashMap) { 020 return (LinkedHashMap<String, Object>) src; 021 } 022 if (src instanceof Map) { 023 return new LinkedHashMap<>((Map) src); 024 } 025 026 try { 027 LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>(); 028 String typeName = src.getClass().getCanonicalName(); 029 if (!cacheDescriptors.containsKey(typeName)) { 030 Map<String, Integer> fieldOrder = buildFieldOrder(src.getClass()); 031 PropertyDescriptor[] allDescriptors = Introspector.getBeanInfo(src.getClass()).getPropertyDescriptors(); 032 List<PropertyDescriptor> validDescriptors = new ArrayList<>(); 033 for (PropertyDescriptor pd : allDescriptors) { 034 if (!"class".equals(pd.getName()) && pd.getReadMethod() != null) { 035 validDescriptors.add(pd); 036 } 037 } 038 validDescriptors.sort(new Comparator<PropertyDescriptor>() { 039 @Override 040 public int compare(PropertyDescriptor o1, PropertyDescriptor o2) { 041 return compareOrder(fieldOrder, o1.getName(), o2.getName()); 042 } 043 }); 044 cacheDescriptors.put(typeName, validDescriptors.toArray(new PropertyDescriptor[0])); 045 } 046 047 for (PropertyDescriptor pd : cacheDescriptors.get(typeName)) { 048 try { 049 JSONField jsonField = pd.getReadMethod().getAnnotation(JSONField.class); 050 if (jsonField != null && !jsonField.serialize()) { 051 continue; 052 } 053 Object value = pd.getReadMethod().invoke(src); 054 if (value == null) { 055 continue; 056 } 057 if (value instanceof Collection) { 058 List<Object> listValues = new ArrayList<>(); 059 for (Object item : (Collection) value) { 060 listValues.add(isBeanOrMap(item) ? bean2Map(item) : item); 061 } 062 resultMap.put(pd.getName(), listValues); 063 } else if (value.getClass().isMemberClass()) { 064 resultMap.put(pd.getName(), bean2Map(value)); 065 } else { 066 resultMap.put(pd.getName(), value); 067 } 068 } catch (Exception ex) { 069 throw new JSONException(ex); 070 } 071 } 072 return resultMap; 073 } catch (IntrospectionException e) { 074 throw new JSONException(e); 075 } 076 } 077 078 /** 079 * Only a bean or a map becomes a map. A null, a scalar or a nested collection or array stays as 080 * it is for the JSON serializer: through bean2Map a null threw, a number came out as {} and a 081 * nested list as its getters, {"empty":false,...} (Mantis 4799). 082 */ 083 private static boolean isBeanOrMap(Object item) { 084 return item != null && !(item instanceof String || item instanceof Number || item instanceof Boolean 085 || item instanceof Character || item instanceof Collection || item.getClass().isArray()); 086 } 087 088 private static Map<String, Integer> buildFieldOrder(Class target) { 089 Map<String, Integer> orderMap = new HashMap<>(); 090 for (Field f : target.getDeclaredFields()) { 091 orderMap.put(f.getName(), orderMap.size()); 092 } 093 return orderMap; 094 } 095 096 private static int compareOrder(Map<String, Integer> orderMap, String n1, String n2) { 097 if (orderMap.containsKey(n1) && orderMap.containsKey(n2)) { 098 return orderMap.get(n1).compareTo(orderMap.get(n2)); 099 } 100 return 0; 101 } 102}