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>> classSettersCache = new ConcurrentHashMap<>(); 035 036 public static <T> T toObject(Row row, Class<T> objectClass) { 037 return toObject(row, objectClass, 0); 038 } 039 040 041 public static <T> T toObject(Row row, Class<T> objectClass, int index) { 042 T instance = ClassUtil.newInstance(objectClass); 043 Map<String, Method> classSetters = getSetterMethods(objectClass); 044 Set<String> rowKeys = row.keySet(); 045 classSetters.forEach((property, setter) -> { 046 try { 047 if (index <= 0) { 048 for (String rowKey : rowKeys) { 049 if (property.equalsIgnoreCase(rowKey)) { 050 Object rowValue = row.get(rowKey); 051 Object value = ConvertUtil.convert(rowValue, setter.getParameterTypes()[0], true); 052 setter.invoke(instance, value); 053 } 054 } 055 } else { 056 for (int i = index; i >= 0; i--) { 057 String newProperty = i <= 0 ? property : property + INDEX_SEPARATOR + i; 058 boolean fillValue = false; 059 for (String rowKey : rowKeys) { 060 if (newProperty.equalsIgnoreCase(rowKey)) { 061 Object rowValue = row.get(rowKey); 062 Object value = ConvertUtil.convert(rowValue, setter.getParameterTypes()[0], true); 063 setter.invoke(instance, value); 064 fillValue = true; 065 break; 066 } 067 } 068 if (fillValue) { 069 break; 070 } 071 } 072 } 073 } catch (Exception e) { 074 throw new RuntimeException("Can not invoke method: " + setter); 075 } 076 }); 077 return instance; 078 } 079 080 081 public static <T> List<T> toObjectList(List<Row> rows, Class<T> objectClass) { 082 return toObjectList(rows, objectClass, 0); 083 } 084 085 086 public static <T> List<T> toObjectList(List<Row> rows, Class<T> objectClass, int index) { 087 if (rows == null || rows.isEmpty()) { 088 return Collections.emptyList(); 089 } else { 090 List<T> objectList = new ArrayList<>(); 091 for (Row row : rows) { 092 objectList.add(toObject(row, objectClass, index)); 093 } 094 return objectList; 095 } 096 } 097 098 099 public static <T> T toEntity(Row row, Class<T> entityClass) { 100 return toEntity(row, entityClass, 0); 101 } 102 103 104 public static <T> T toEntity(Row row, Class<T> entityClass, int index) { 105 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 106 return tableInfo.newInstanceByRow(row, index); 107 } 108 109 110 public static <T> List<T> toEntityList(List<Row> rows, Class<T> entityClass) { 111 return toEntityList(rows, entityClass, 0); 112 } 113 114 115 public static <T> List<T> toEntityList(List<Row> rows, Class<T> entityClass, int index) { 116 if (rows == null || rows.isEmpty()) { 117 return Collections.emptyList(); 118 } else { 119 TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass); 120 List<T> entityList = new ArrayList<>(); 121 for (Row row : rows) { 122 T entity = tableInfo.newInstanceByRow(row, index); 123 entityList.add(entity); 124 } 125 return entityList; 126 } 127 } 128 129 130 public static void registerMapping(Class<?> clazz, Map<String, Method> columnSetterMapping) { 131 classSettersCache.put(clazz, columnSetterMapping); 132 } 133 134 135 public static void printPretty(Row row) { 136 printPretty(Collections.singletonList(row)); 137 } 138 139 140 public static void printPretty(List<Row> rows) { 141 if (rows == null || rows.isEmpty()) { 142 return; 143 } 144 145 Row firstRow = rows.get(0); 146 List<Integer> textConsoleLengthList = new ArrayList<>(); 147 StringBuilder sb = new StringBuilder("\nTotal Count: " + rows.size() + "\n"); 148 Set<String> keys = firstRow.keySet(); 149 keys.forEach(s -> { 150 String sa = "|" + s + " "; 151 sb.append(sa); 152 textConsoleLengthList.add(calcTextConsoleLength(sa)); 153 }); 154 sb.append("|\n"); 155 156 rows.forEach(row -> { 157 int i = 0; 158 for (String key : keys) { 159 sb.append(getColString(row.get(key), textConsoleLengthList.get(i))); 160 i++; 161 } 162 sb.append("|\n"); 163 }); 164 165 System.out.println(sb); 166 } 167 168 169 private static String getColString(Object o, int len) { 170 String v = "|" + o; 171 while (calcTextConsoleLength(v) < len) { 172 v += " "; 173 } 174 175 while (calcTextConsoleLength(v) > len) { 176 v = v.substring(0, v.length() - 5) + "... "; 177 } 178 return v; 179 } 180 181 182 private static int calcTextConsoleLength(String s) { 183 int result = 0; 184 char[] chars = s.toCharArray(); 185 for (char c : chars) { 186 if (isCJK(c)) { 187 result += 3; 188 } else { 189 result += 2; 190 } 191 } 192 return result % 2 != 0 ? result + 1 : result; 193 } 194 195 196 private static boolean isCJK(char c) { 197 Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); 198 return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS 199 || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B 200 || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS 201 || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION; 202 } 203 204 205 private static Map<String, Method> getSetterMethods(Class<?> aClass) { 206 return MapUtil.computeIfAbsent(classSettersCache, aClass, aClass1 -> { 207 Map<String, Method> columnSetterMapping = new HashMap<>(); 208 List<Method> setters = ClassUtil.getAllMethods(aClass1, 209 method -> method.getName().startsWith("set") 210 && method.getParameterCount() == 1 211 && Modifier.isPublic(method.getModifiers()) 212 ); 213 for (Method setter : setters) { 214 String column = setter.getName().substring(3); 215 columnSetterMapping.put(column, setter); 216 columnSetterMapping.put(StringUtil.camelToUnderline(column), setter); 217 columnSetterMapping.put(StringUtil.underlineToCamel(column), setter); 218 } 219 return columnSetterMapping; 220 }); 221 } 222}