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}