001package gudusoft.gsqlparser.dlineage.dataflow.metadata.sqlflow; 002 003import gudusoft.gsqlparser.dlineage.metadata.Coordinate; 004import gudusoft.gsqlparser.dlineage.metadata.Server; 005import gudusoft.gsqlparser.dlineage.metadata.Sqlflow; 006import gudusoft.gsqlparser.dlineage.metadata.TreeNode; 007import gudusoft.gsqlparser.util.json.JSON; 008import gudusoft.gsqlparser.util.json.JSONField; 009 010import java.lang.reflect.Array; 011import java.lang.reflect.Method; 012import java.lang.reflect.Modifier; 013import java.lang.reflect.ParameterizedType; 014import java.lang.reflect.Type; 015import java.util.ArrayList; 016import java.util.LinkedHashMap; 017import java.util.List; 018import java.util.Map; 019 020public final class SqlflowObjectAdapter { 021 022 private SqlflowObjectAdapter() { 023 } 024 025 public static Sqlflow fromJson(String json) { 026 return fromMap((Map) JSON.parseObject(json)); 027 } 028 029 public static Sqlflow fromMap(Map metadata) { 030 Sqlflow sqlflow = bind(metadata, Sqlflow.class, null, null); 031 restoreTreeReferences(sqlflow, null, null); 032 return sqlflow; 033 } 034 035 private static <T> T bind(Map values, Class<T> type, TreeNode parent, Server server) { 036 try { 037 T target = type.newInstance(); 038 for (Method method : type.getMethods()) { 039 if (!isSetter(method)) { 040 continue; 041 } 042 String property = propertyName(method.getName()); 043 Object value = values.get(property); 044 if (value == null) { 045 continue; 046 } 047 method.invoke(target, bindValue(value, method.getParameterTypes()[0], method.getGenericParameterTypes()[0], parent, server)); 048 } 049 return target; 050 } catch (Exception e) { 051 throw new IllegalArgumentException("Cannot bind sqlflow metadata " + type.getName(), e); 052 } 053 } 054 055 private static Object bindValue(Object value, Class<?> type, Type genericType, TreeNode parent, Server server) { 056 if (value == null) { 057 return null; 058 } 059 if (type == String.class || type == Boolean.class || type == boolean.class) { 060 return value; 061 } 062 if (type.isArray() && value instanceof String && type.getComponentType() == Coordinate.class) { 063 return Coordinate.parse((String) value); 064 } 065 if (value instanceof Number) { 066 Number number = (Number) value; 067 if (type == Integer.class || type == int.class) return number.intValue(); 068 if (type == Long.class || type == long.class) return number.longValue(); 069 if (type == Double.class || type == double.class) return number.doubleValue(); 070 if (type == Float.class || type == float.class) return number.floatValue(); 071 if (type == Short.class || type == short.class) return number.shortValue(); 072 if (type == Byte.class || type == byte.class) return number.byteValue(); 073 } 074 if (type.isArray()) { 075 List values = (List) value; 076 Object array = Array.newInstance(type.getComponentType(), values.size()); 077 for (int i = 0; i < values.size(); i++) { 078 Array.set(array, i, bindValue(values.get(i), type.getComponentType(), type.getComponentType(), parent, server)); 079 } 080 return array; 081 } 082 if (List.class.isAssignableFrom(type)) { 083 Type elementType = ((ParameterizedType) genericType).getActualTypeArguments()[0]; 084 Class<?> elementClass = (Class<?>) elementType; 085 List<Object> bound = new ArrayList<Object>(); 086 for (Object item : (List) value) { 087 bound.add(bindValue(item, elementClass, elementClass, parent, server)); 088 } 089 return bound; 090 } 091 return bind((Map) value, type, parent, server); 092 } 093 094 private static void restoreTreeReferences(Object value, TreeNode parent, Server server) { 095 if (value == null) { 096 return; 097 } 098 if (value instanceof Sqlflow) { 099 List<Server> servers = ((Sqlflow) value).getServers(); 100 if (servers != null) { 101 for (Server item : servers) { 102 restoreTreeReferences(item, null, item); 103 } 104 } 105 return; 106 } 107 if (value instanceof TreeNode) { 108 TreeNode node = (TreeNode) value; 109 node.setParent(parent); 110 node.setServer(server); 111 parent = node; 112 } 113 for (Method method : value.getClass().getMethods()) { 114 if (!isSerializableGetter(method)) { 115 continue; 116 } 117 try { 118 Object child = method.invoke(value); 119 if (child instanceof Iterable) { 120 for (Object item : (Iterable) child) { 121 restoreTreeReferences(item, parent, server); 122 } 123 } else if (child != null && child.getClass().isArray()) { 124 for (int i = 0; i < Array.getLength(child); i++) { 125 restoreTreeReferences(Array.get(child, i), parent, server); 126 } 127 } else if (child instanceof TreeNode) { 128 restoreTreeReferences(child, parent, server); 129 } 130 } catch (Exception e) { 131 throw new IllegalArgumentException("Cannot restore sqlflow metadata references", e); 132 } 133 } 134 } 135 136 public static Map<String, Object> toMap(Object value) { 137 return (Map<String, Object>) convert(value); 138 } 139 140 private static Object convert(Object value) { 141 if (value == null || value instanceof String || value instanceof Number || value instanceof Boolean) { 142 return value; 143 } 144 if (value instanceof Enum) { 145 return ((Enum) value).name(); 146 } 147 if (value instanceof Map) { 148 Map<Object, Object> converted = new LinkedHashMap<Object, Object>(); 149 for (Object entryObject : ((Map) value).entrySet()) { 150 Map.Entry entry = (Map.Entry) entryObject; 151 converted.put(entry.getKey(), convert(entry.getValue())); 152 } 153 return converted; 154 } 155 if (value instanceof Iterable) { 156 List<Object> converted = new ArrayList<Object>(); 157 for (Object item : (Iterable) value) { 158 converted.add(convert(item)); 159 } 160 return converted; 161 } 162 if (value.getClass().isArray()) { 163 List<Object> converted = new ArrayList<Object>(); 164 for (int i = 0; i < Array.getLength(value); i++) { 165 converted.add(convert(Array.get(value, i))); 166 } 167 return converted; 168 } 169 170 Map<String, Object> converted = new LinkedHashMap<String, Object>(); 171 for (Method method : value.getClass().getMethods()) { 172 if (!isSerializableGetter(method)) { 173 continue; 174 } 175 try { 176 Object propertyValue = method.invoke(value); 177 if (propertyValue != null) { 178 converted.put(propertyName(method.getName()), convert(propertyValue)); 179 } 180 } catch (Exception e) { 181 throw new IllegalArgumentException("Cannot read sqlflow metadata property " + method.getName(), e); 182 } 183 } 184 return converted; 185 } 186 187 private static boolean isSetter(Method method) { 188 return Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("set") 189 && method.getParameterTypes().length == 1; 190 } 191 192 private static boolean isSerializableGetter(Method method) { 193 if (!Modifier.isPublic(method.getModifiers()) || method.getParameterTypes().length != 0 194 || "getClass".equals(method.getName()) || (!method.getName().startsWith("get") 195 && !method.getName().startsWith("is"))) { 196 return false; 197 } 198 JSONField jsonField = method.getAnnotation(JSONField.class); 199 if (jsonField != null && !jsonField.serialize()) { 200 return false; 201 } 202 return true; 203 } 204 205 private static String propertyName(String getterName) { 206 int prefixLength = getterName.startsWith("is") ? 2 : 3; 207 String name = getterName.substring(prefixLength); 208 return Character.toLowerCase(name.charAt(0)) + name.substring(1); 209 } 210}