001/** 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.row; 017 018import com.mybatisflex.core.table.TableInfo; 019import com.mybatisflex.core.table.TableInfoFactory; 020import com.mybatisflex.core.util.ClassUtil; 021import com.mybatisflex.core.util.ConvertUtil; 022import com.mybatisflex.core.util.StringUtil; 023import org.apache.ibatis.util.MapUtil; 024 025import java.lang.reflect.Method; 026import java.lang.reflect.Modifier; 027import java.util.*; 028import java.util.concurrent.ConcurrentHashMap; 029 030public class RowUtil { 031 032 static final String INDEX_SEPARATOR = "$"; 033 034 private static final Map<Class<?>, Map<String, Method>> classGettersMapping = new ConcurrentHashMap<>(); 035 036 037 public static <T> T toObject(Row row, Class<T> objectClass) { 038 return toObject(row, objectClass, 0); 039 } 040 041 042 public static <T> T toObject(Row row, Class<T> objectClass, int index) { 043 T instance = ClassUtil.newInstance(objectClass); 044 Map<String, Method> setterMethods = getSetterMethods(objectClass); 045 Set<String> rowKeys = row.keySet(); 046 setterMethods.forEach((property, setter) -> { 047 try { 048 if (index <= 0) { 049 for (String rowKey : rowKeys) { 050 if (property.equalsIgnoreCase(rowKey)) { 051 Object rowValue = row.get(rowKey); 052 Object value = ConvertUtil.convert(rowValue, setter.getParameterTypes()[0]); 053 setter.invoke(instance, value); 054 } 055 } 056 } else { 057 for (int i = index; i >= 0; i--) { 058 String newProperty = i <= 0 ? property : property + INDEX_SEPARATOR + i; 059 boolean fillValue = false; 060 for (String rowKey : rowKeys) { 061 if (newProperty.equalsIgnoreCase(rowKey)) { 062 Object rowValue = row.get(rowKey); 063 Object value = ConvertUtil.convert(rowValue, setter.getParameterTypes()[0]); 064 setter.invoke(instance, value); 065 fillValue = true; 066 break; 067 } 068 } 069 if (fillValue) { 070 break; 071 } 072 } 073 } 074 } catch (Exception e) { 075 throw new RuntimeException("Can not invoke method: " + setter); 076 } 077 }); 078 return instance; 079 } 080 081 082 public static <T> List<T> toObjectList(List<Row> rows, Class<T> objectClass) { 083 return toObjectList(rows, objectClass, 0); 084 } 085 086 087 public static <T> List<T> toObjectList(List<Row> rows, Class<T> objectClass, int index) { 088 if (rows == null || rows.isEmpty()) { 089 return Collections.emptyList(); 090 } else { 091 List<T> objectList = new ArrayList<>(); 092 for (Row row : rows) { 093 objectList.add(toObject(row, objectClass, index)); 094 } 095 return objectList; 096 } 097 } 098 099 100 public static <T> T toEntity(Row row, Class<T> entityClass) { 101 return toEntity(row, entityClass, 0); 102 } 103 104 105 public static <T> T toEntity(Row row, Class<T> entityClass, int index) { 106 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 107 return tableInfo.newInstanceByRow(row, index); 108 } 109 110 111 public static <T> List<T> toEntityList(List<Row> rows, Class<T> entityClass) { 112 return toEntityList(rows, entityClass, 0); 113 } 114 115 116 public static <T> List<T> toEntityList(List<Row> rows, Class<T> entityClass, int index) { 117 if (rows == null || rows.isEmpty()) { 118 return Collections.emptyList(); 119 } else { 120 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 121 List<T> entityList = new ArrayList<>(); 122 for (Row row : rows) { 123 T entity = tableInfo.newInstanceByRow(row, index); 124 entityList.add(entity); 125 } 126 return entityList; 127 } 128 } 129 130 131 public static void registerMapping(Class<?> clazz, Map<String, Method> columnSetterMapping) { 132 classGettersMapping.put(clazz, columnSetterMapping); 133 } 134 135 136 public static void printPretty(Row row) { 137 printPretty(Collections.singletonList(row)); 138 } 139 140 141 public static void printPretty(List<Row> rows) { 142 if (rows == null || rows.isEmpty()) { 143 return; 144 } 145 146 Row firstRow = rows.get(0); 147 List<Integer> textConsoleLengthList = new ArrayList<>(); 148 StringBuilder sb = new StringBuilder("\nTotal Count: " + rows.size() + "\n"); 149 Set<String> keys = firstRow.keySet(); 150 keys.forEach(s -> { 151 String sa = "|" + s + " "; 152 sb.append(sa); 153 textConsoleLengthList.add(calcTextConsoleLength(sa)); 154 }); 155 sb.append("|\n"); 156 157 rows.forEach(row -> { 158 int i = 0; 159 for (String key : keys) { 160 sb.append(getColString(row.get(key), textConsoleLengthList.get(i))); 161 i++; 162 } 163 sb.append("|\n"); 164 }); 165 166 System.out.println(sb); 167 } 168 169 170 private static String getColString(Object o, int len) { 171 String v = "|" + o; 172 while (calcTextConsoleLength(v) < len) { 173 v += " "; 174 } 175 176 while (calcTextConsoleLength(v) > len) { 177 v = v.substring(0, v.length() - 5) + "... "; 178 } 179 return v; 180 } 181 182 183 private static int calcTextConsoleLength(String s) { 184 int result = 0; 185 char[] chars = s.toCharArray(); 186 for (char c : chars) { 187 if (isCJK(c)) { 188 result += 3; 189 } else { 190 result += 2; 191 } 192 } 193 return result % 2 != 0 ? result + 1 : result; 194 } 195 196 197 private static boolean isCJK(char c) { 198 Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); 199 return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS 200 || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B 201 || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS 202 || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION; 203 } 204 205 206 private static Map<String, Method> getSetterMethods(Class<?> aClass) { 207 return MapUtil.computeIfAbsent(classGettersMapping, aClass, aClass1 -> { 208 Map<String, Method> columnSetterMapping = new HashMap<>(); 209 List<Method> setters = ClassUtil.getAllMethods(aClass1, 210 method -> method.getName().startsWith("set") 211 && method.getParameterCount() == 1 212 && Modifier.isPublic(method.getModifiers()) 213 ); 214 for (Method setter : setters) { 215 String column = setter.getName().substring(3); 216 columnSetterMapping.put(column, setter); 217 columnSetterMapping.put(StringUtil.camelToUnderline(column), setter); 218 columnSetterMapping.put(StringUtil.underlineToCamel(column), setter); 219 } 220 return columnSetterMapping; 221 }); 222 } 223}