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 firstRow.keySet().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 (Object value : row.values()) { 159 sb.append(getColString(value, 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(classGettersMapping, 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}