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