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