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.util; 017 018import com.mybatisflex.annotation.EnumValue; 019 020import java.lang.reflect.Field; 021import java.lang.reflect.Method; 022import java.lang.reflect.Modifier; 023import java.math.BigDecimal; 024import java.math.BigInteger; 025import java.time.LocalDate; 026import java.time.LocalDateTime; 027import java.time.LocalTime; 028import java.time.temporal.Temporal; 029import java.util.Date; 030import java.util.List; 031import java.util.Objects; 032 033public class ConvertUtil { 034 035 036 public static Object convert(Object value, Class targetClass) { 037 return convert(value, targetClass, false); 038 } 039 040 public static Object convert(Object value, Class targetClass, boolean ignoreConvertError) { 041 if (value == null && targetClass.isPrimitive()) { 042 return getPrimitiveDefaultValue(targetClass); 043 } 044 if (value == null || (targetClass != String.class && value.getClass() == String.class 045 && StringUtil.isBlank((String) value))) { 046 return null; 047 } 048 if (value.getClass().isAssignableFrom(targetClass)) { 049 return value; 050 } 051 if (targetClass == String.class) { 052 return value.toString(); 053 } else if (targetClass == Integer.class || targetClass == int.class) { 054 if (value instanceof Number) { 055 return ((Number) value).intValue(); 056 } 057 return Integer.parseInt(value.toString()); 058 } else if (targetClass == Long.class || targetClass == long.class) { 059 if (value instanceof Number) { 060 return ((Number) value).longValue(); 061 } 062 return Long.parseLong(value.toString()); 063 } else if (targetClass == Double.class || targetClass == double.class) { 064 if (value instanceof Number) { 065 return ((Number) value).doubleValue(); 066 } 067 return Double.parseDouble(value.toString()); 068 } else if (targetClass == Float.class || targetClass == float.class) { 069 if (value instanceof Number) { 070 return ((Number) value).floatValue(); 071 } 072 return Float.parseFloat(value.toString()); 073 } else if (targetClass == Boolean.class || targetClass == boolean.class) { 074 String v = value.toString().toLowerCase(); 075 if ("1".equals(v) || "true".equalsIgnoreCase(v)) { 076 return Boolean.TRUE; 077 } else if ("0".equals(v) || "false".equalsIgnoreCase(v)) { 078 return Boolean.FALSE; 079 } else { 080 throw new RuntimeException("Can not parse to boolean type of value: \"" + value + "\""); 081 } 082 } else if (targetClass == java.math.BigDecimal.class) { 083 return new java.math.BigDecimal(value.toString()); 084 } else if (targetClass == java.math.BigInteger.class) { 085 return new java.math.BigInteger(value.toString()); 086 } else if (targetClass == byte[].class) { 087 return value.toString().getBytes(); 088 } else if (targetClass == Date.class) { 089 return DateUtil.parseDate(value); 090 } else if (targetClass == LocalDateTime.class) { 091 return toLocalDateTime(value); 092 } else if (targetClass == LocalDate.class) { 093 return DateUtil.toLocalDate(DateUtil.parseDate(value)); 094 } else if (targetClass == LocalTime.class) { 095 return DateUtil.toLocalTime(DateUtil.parseDate(value)); 096 } else if (targetClass == Short.class || targetClass == short.class) { 097 if (value instanceof Number) { 098 return ((Number) value).shortValue(); 099 } 100 return Short.parseShort(value.toString()); 101 } else if (targetClass.isEnum()) { 102 Object[] enumConstants = targetClass.getEnumConstants(); 103 List<Field> allFields = ClassUtil.getAllFields(targetClass, field -> field.getAnnotation(EnumValue.class) != null); 104 if (allFields.size() == 1) { 105 Field field = allFields.get(0); 106 107 String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName()); 108 List<Method> allMethods = ClassUtil.getAllMethods(targetClass, method -> { 109 String methodName = method.getName(); 110 return methodName.equals(fieldGetterName); 111 }); 112 113 //getter 114 if (allMethods.size() == 1) { 115 Method getter = allMethods.get(0); 116 for (Object enumConstant : enumConstants) { 117 try { 118 Object enumValue = getter.invoke(enumConstant); 119 if (Objects.equals(enumValue, value)) { 120 return enumConstant; 121 } 122 } catch (Exception e) { 123 throw new RuntimeException(e); 124 } 125 } 126 } 127 128 //public field 129 else if (Modifier.isPublic(field.getModifiers())) { 130 for (Object enumConstant : enumConstants) { 131 if (Objects.equals(readPublicField(field, enumConstant), value)) { 132 return enumConstant; 133 } 134 } 135 } 136 } else if (value instanceof String) { 137 return Enum.valueOf(targetClass, value.toString()); 138 } 139 } 140 141 if (ignoreConvertError) { 142 return null; 143 } else { 144 throw new IllegalArgumentException("Can not convert \"" + value + "\" to type\"" + targetClass.getName() + "\"."); 145 } 146 } 147 148 149 private static Object readPublicField(Field field, Object target) { 150 try { 151 return field.get(target); 152 } catch (IllegalAccessException e) { 153 throw new RuntimeException(e); 154 } 155 } 156 157 158 //Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE 159 public static Object getPrimitiveDefaultValue(Class<?> paraClass) { 160 if (paraClass == int.class || paraClass == long.class || paraClass == float.class || paraClass == double.class) { 161 return 0; 162 } else if (paraClass == boolean.class) { 163 return Boolean.FALSE; 164 } else if (paraClass == short.class) { 165 return (short) 0; 166 } else if (paraClass == byte.class) { 167 return (byte) 0; 168 } else if (paraClass == char.class) { 169 return '\u0000'; 170 } else { 171 throw new IllegalArgumentException("Can not get primitive default value for type: " + paraClass); 172 } 173 } 174 175 176 public static Integer toInt(Object i) { 177 if (i instanceof Integer) { 178 return (Integer) i; 179 } else if (i instanceof Number) { 180 return ((Number) i).intValue(); 181 } 182 return i != null ? Integer.parseInt(i.toString()) : null; 183 } 184 185 public static Long toLong(Object l) { 186 if (l instanceof Long) { 187 return (Long) l; 188 } else if (l instanceof Number) { 189 return ((Number) l).longValue(); 190 } 191 return l != null ? Long.parseLong(l.toString()) : null; 192 } 193 194 public static Double toDouble(Object d) { 195 if (d instanceof Double) { 196 return (Double) d; 197 } else if (d instanceof Number) { 198 return ((Number) d).doubleValue(); 199 } 200 201 return d != null ? Double.parseDouble(d.toString()) : null; 202 } 203 204 public static BigDecimal toBigDecimal(Object b) { 205 if (b instanceof BigDecimal) { 206 return (BigDecimal) b; 207 } else if (b != null) { 208 return new BigDecimal(b.toString()); 209 } else { 210 return null; 211 } 212 } 213 214 public static BigInteger toBigInteger(Object b) { 215 if (b instanceof BigInteger) { 216 return (BigInteger) b; 217 } 218 // 数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal, 219 // 但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用 220 if (b instanceof BigDecimal) { 221 return ((BigDecimal) b).toBigInteger(); 222 } else if (b instanceof Number) { 223 return BigInteger.valueOf(((Number) b).longValue()); 224 } else if (b instanceof String) { 225 return new BigInteger((String) b); 226 } 227 228 return (BigInteger) b; 229 } 230 231 public static Float toFloat(Object f) { 232 if (f instanceof Float) { 233 return (Float) f; 234 } else if (f instanceof Number) { 235 return ((Number) f).floatValue(); 236 } 237 return f != null ? Float.parseFloat(f.toString()) : null; 238 } 239 240 241 public static Short toShort(Object s) { 242 if (s instanceof Short) { 243 return (Short) s; 244 } else if (s instanceof Number) { 245 return ((Number) s).shortValue(); 246 } 247 return s != null ? Short.parseShort(s.toString()) : null; 248 } 249 250 251 public static Byte toByte(Object b) { 252 if (b instanceof Byte) { 253 return (Byte) b; 254 } else if (b instanceof Number) { 255 return ((Number) b).byteValue(); 256 } 257 return b != null ? Byte.parseByte(b.toString()) : null; 258 } 259 260 public static Boolean toBoolean(Object b) { 261 if (b instanceof Boolean) { 262 return (Boolean) b; 263 } else if (b == null) { 264 return null; 265 } 266 267 // 支持 Number 之下的整数类型 268 if (b instanceof Number) { 269 int n = ((Number) b).intValue(); 270 if (n == 1) { 271 return Boolean.TRUE; 272 } else if (n == 0) { 273 return Boolean.FALSE; 274 } 275 throw new IllegalArgumentException("Can not support convert: \"" + b + "\" to boolean."); 276 } 277 278 // 支持 String 279 if (b instanceof String) { 280 String s = b.toString(); 281 if ("true".equalsIgnoreCase(s) || "1".equals(s)) { 282 return Boolean.TRUE; 283 } else if ("false".equalsIgnoreCase(s) || "0".equals(s)) { 284 return Boolean.FALSE; 285 } 286 } 287 288 return (Boolean) b; 289 } 290 291 public static Number toNumber(Object o) { 292 if (o instanceof Number) { 293 return (Number) o; 294 } else if (o == null) { 295 return null; 296 } 297 String s = o.toString(); 298 return s.indexOf('.') != -1 ? Double.parseDouble(s) : Long.parseLong(s); 299 } 300 301 302 public static Date toDate(Object o) { 303 if (o instanceof Date) { 304 return (Date) o; 305 } 306 307 if (o instanceof Temporal) { 308 if (o instanceof LocalDateTime) { 309 return DateUtil.toDate((LocalDateTime) o); 310 } 311 if (o instanceof LocalDate) { 312 return DateUtil.toDate((LocalDate) o); 313 } 314 if (o instanceof LocalTime) { 315 return DateUtil.toDate((LocalTime) o); 316 } 317 } 318 319 if (o instanceof String) { 320 String s = (String) o; 321 return DateUtil.parseDate(s); 322 } 323 324 return (java.util.Date) o; 325 } 326 327 328 public static LocalDateTime toLocalDateTime(Object o) { 329 if (o instanceof LocalDateTime) { 330 return (LocalDateTime) o; 331 } 332 if (o instanceof java.util.Date) { 333 return DateUtil.toLocalDateTime((java.util.Date) o); 334 } 335 if (o instanceof LocalDate) { 336 return ((LocalDate) o).atStartOfDay(); 337 } 338 if (o instanceof LocalTime) { 339 return LocalDateTime.of(LocalDate.now(), (LocalTime) o); 340 } 341 342 if (o instanceof String) { 343 String s = (String) o; 344 return DateUtil.parseLocalDateTime(s); 345 } 346 347 return (LocalDateTime) o; 348 } 349}