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