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