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