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}