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