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