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
132    public static Integer toInt(Object i) {
133        if (i instanceof Integer) {
134            return (Integer) i;
135        } else if (i instanceof Number) {
136            return ((Number) i).intValue();
137        }
138        return i != null ? Integer.parseInt(i.toString()) : null;
139    }
140
141    public static Long toLong(Object l) {
142        if (l instanceof Long) {
143            return (Long) l;
144        } else if (l instanceof Number) {
145            return ((Number) l).longValue();
146        }
147        return l != null ? Long.parseLong(l.toString()) : null;
148    }
149
150    public static Double toDouble(Object d) {
151        if (d instanceof Double) {
152            return (Double) d;
153        } else if (d instanceof Number) {
154            return ((Number) d).doubleValue();
155        }
156
157        return d != null ? Double.parseDouble(d.toString()) : null;
158    }
159
160    public static BigDecimal toBigDecimal(Object b) {
161        if (b instanceof BigDecimal) {
162            return (BigDecimal) b;
163        } else if (b != null) {
164            return new BigDecimal(b.toString());
165        } else {
166            return null;
167        }
168    }
169
170    public static BigInteger toBigInteger(Object b) {
171        if (b instanceof BigInteger) {
172            return (BigInteger) b;
173        }
174        // 数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal,
175        // 但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用
176        if (b instanceof BigDecimal) {
177            return ((BigDecimal) b).toBigInteger();
178        } else if (b instanceof Number) {
179            return BigInteger.valueOf(((Number) b).longValue());
180        } else if (b instanceof String) {
181            return new BigInteger((String) b);
182        }
183
184        return (BigInteger) b;
185    }
186
187    public static Float toFloat(Object f) {
188        if (f instanceof Float) {
189            return (Float) f;
190        } else if (f instanceof Number) {
191            return ((Number) f).floatValue();
192        }
193        return f != null ? Float.parseFloat(f.toString()) : null;
194    }
195
196
197    public static Short toShort(Object s) {
198        if (s instanceof Short) {
199            return (Short) s;
200        } else if (s instanceof Number) {
201            return ((Number) s).shortValue();
202        }
203        return s != null ? Short.parseShort(s.toString()) : null;
204    }
205
206
207    public static Byte toByte(Object b) {
208        if (b instanceof Byte) {
209            return (Byte) b;
210        } else if (b instanceof Number) {
211            return ((Number) b).byteValue();
212        }
213        return b != null ? Byte.parseByte(b.toString()) : null;
214    }
215
216    public static Boolean toBoolean(Object b) {
217        if (b instanceof Boolean) {
218            return (Boolean) b;
219        } else if (b == null) {
220            return null;
221        }
222
223        // 支持 Number 之下的整数类型
224        if (b instanceof Number) {
225            int n = ((Number) b).intValue();
226            if (n == 1) {
227                return Boolean.TRUE;
228            } else if (n == 0) {
229                return Boolean.FALSE;
230            }
231            throw new IllegalArgumentException("Can not support convert: \"" + b + "\" to boolean.");
232        }
233
234        // 支持 String
235        if (b instanceof String) {
236            String s = b.toString();
237            if ("true".equalsIgnoreCase(s) || "1".equals(s)) {
238                return Boolean.TRUE;
239            } else if ("false".equalsIgnoreCase(s) || "0".equals(s)) {
240                return Boolean.FALSE;
241            }
242        }
243
244        return (Boolean) b;
245    }
246
247
248    public static Date toDate(Object o) {
249        if (o instanceof Date) {
250            return (Date) o;
251        }
252
253        if (o instanceof Temporal) {
254            if (o instanceof LocalDateTime) {
255                return DateUtil.toDate((LocalDateTime) o);
256            }
257            if (o instanceof LocalDate) {
258                return DateUtil.toDate((LocalDate) o);
259            }
260            if (o instanceof LocalTime) {
261                return DateUtil.toDate((LocalTime) o);
262            }
263        }
264
265        if (o instanceof String) {
266            String s = (String) o;
267            return DateUtil.parseDate(s);
268        }
269
270        return (java.util.Date) o;
271    }
272
273
274    public static LocalDateTime toLocalDateTime(Object o) {
275        if (o instanceof LocalDateTime) {
276            return (LocalDateTime) o;
277        }
278        if (o instanceof java.util.Date) {
279            return DateUtil.toLocalDateTime((java.util.Date) o);
280        }
281        if (o instanceof LocalDate) {
282            return ((LocalDate) o).atStartOfDay();
283        }
284        if (o instanceof LocalTime) {
285            return LocalDateTime.of(LocalDate.now(), (LocalTime) o);
286        }
287
288        if (o instanceof String) {
289            String s = (String) o;
290            return DateUtil.parseLocalDateTime(s);
291        }
292
293        return (LocalDateTime) o;
294    }
295
296}