diff --git a/README.md b/README.md
index 89bfddcf9a2122115fbb79505aef791ad1b04f87..84dd5e3ffdcbac7009fec8064f386c2eff6681c2 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,6 @@
](https://gitee.com/vonchange/spring-data-mybatis-mini)
-
**等同于spring data jdbc + mybatis 动态sql能力**
**大道至简 极致效率 麻雀虽小 五脏俱全**
@@ -35,6 +34,36 @@ close=")">#{item}
```

+== 新增融合mybatis-spring-boot实现 只简化mybatis动态sql写法和sql写在markdown文件里
+```
+-- 依赖 详情见mybatis-sql-extend-test模块
+
+ com.vonchange.common
+ mybatis-sql-extend
+ ${spring.mybatis.mini}
+
+```
+```
+ public class SimpleLanguageDriver extends XMLLanguageDriver implements LanguageDriver {
+ @Override
+ public SqlSource createSqlSource(Configuration configuration, String script, Class> parameterType) {
+
+ String sqlInXml = MybatisSqlLanguageUtil.sqlInXml("mapper",script,new MySQLDialect());
+ return super.createSqlSource(configuration, sqlInXml, parameterType);
+ }
+}
+```
+
+```
+-- 配置
+mybatis:
+ default-scripting-language-driver: com.vonchange.mybatis.test.config.SimpleLanguageDriver
+ configuration:
+ map-underscore-to-camel-case: true
+
+```
+
+
== Getting Started
1. 提供单表增删改(没有物理删除) 批量更新插入等基础方法
diff --git a/common-util/pom.xml b/common-util/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ee87c9695add01a49e2f2aff3901b692c5cffa90
--- /dev/null
+++ b/common-util/pom.xml
@@ -0,0 +1,28 @@
+
+
+
+ mybatis-mini-parant
+ com.vonchange.common
+ 2.3.9
+
+ 4.0.0
+
+ common-util
+ 2.3.9
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.8
+ provided
+
+
+
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/Assert.java b/common-util/src/main/java/com/vonchange/common/util/Assert.java
new file mode 100644
index 0000000000000000000000000000000000000000..187d68a3359f924803e799cd0e6e4acde016dab2
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/Assert.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2002-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.vonchange.common.util;
+
+import java.util.function.Supplier;
+
+/**
+ * Assertion utility class that assists in validating arguments.
+ *
+ *
Useful for identifying programmer errors early and clearly at runtime.
+ *
+ *
For example, if the contract of a public method states it does not
+ * allow {@code null} arguments, {@code Assert} can be used to validate that
+ * contract. Doing this clearly indicates a contract violation when it
+ * occurs and protects the class's invariants.
+ *
+ *
Typically used to validate method arguments rather than configuration
+ * properties, to check for cases that are usually programmer errors rather
+ * than configuration errors. In contrast to configuration initialization
+ * code, there is usually no point in falling back to defaults in such methods.
+ *
+ *
This class is similar to JUnit's assertion library. If an argument value is
+ * deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
+ * For example:
+ *
+ *
+ * Assert.notNull(clazz, "The class must not be null");
+ * Assert.isTrue(i > 0, "The value must be greater than zero");
+ *
+ *
Mainly for internal use within the framework; consider
+ * Apache's Commons Lang
+ * for a more comprehensive suite of {@code String} utilities.
+ *
+ * @author Keith Donald
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @author Colin Sampaleanu
+ * @author Rob Harrop
+ * @since 1.1.2
+ */
+public abstract class Assert {
+ private Assert(){
+ throw new IllegalStateException("Utility class");
+ }
+
+ /**
+ * Assert a boolean expression, throwing an {@code IllegalStateException}
+ * if the expression evaluates to {@code false}.
+ *
Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
+ * on an assertion failure.
+ *
Assert.state(id == null, "The id property must not already be initialized");
+ * @param expression a boolean expression
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalStateException if {@code expression} is {@code false}
+ */
+ public static void state(boolean expression, String message) {
+ if (!expression) {
+ throw new IllegalStateException(message);
+ }
+ }
+
+ /**
+ * Assert a boolean expression, throwing an {@code IllegalStateException}
+ * if the expression evaluates to {@code false}.
+ *
Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
+ * on an assertion failure.
+ *
+ * Assert.state(id == null,
+ * () -> "ID for " + entity.getName() + " must not already be initialized");
+ *
+ * @param expression a boolean expression
+ * @param messageSupplier a supplier for the exception message to use if the
+ * assertion fails
+ * @throws IllegalStateException if {@code expression} is {@code false}
+ * @since 5.0
+ */
+ public static void state(boolean expression, Supplier messageSupplier) {
+ if (!expression) {
+ throw new IllegalStateException(nullSafeGet(messageSupplier));
+ }
+ }
+
+
+
+ /**
+ * Assert a boolean expression, throwing an {@code IllegalArgumentException}
+ * if the expression evaluates to {@code false}.
+ *
Assert.isTrue(i > 0, "The value must be greater than zero");
+ * @param expression a boolean expression
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if {@code expression} is {@code false}
+ */
+ public static void isTrue(boolean expression, String message) {
+ if (!expression) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert a boolean expression, throwing an {@code IllegalArgumentException}
+ * if the expression evaluates to {@code false}.
+ *
+ * Assert.isTrue(i > 0, () -> "The value '" + i + "' must be greater than zero");
+ *
+ * @param expression a boolean expression
+ * @param messageSupplier a supplier for the exception message to use if the
+ * assertion fails
+ * @throws IllegalArgumentException if {@code expression} is {@code false}
+ * @since 5.0
+ */
+ public static void isTrue(boolean expression, Supplier messageSupplier) {
+ if (!expression) {
+ throw new IllegalArgumentException(nullSafeGet(messageSupplier));
+ }
+ }
+
+
+
+ /**
+ * Assert that an object is {@code null}.
+ *
Assert.isNull(value, "The value must be null");
+ * @param object the object to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object is not {@code null}
+ */
+ public static void isNull( Object object, String message) {
+ if (object != null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that an object is {@code null}.
+ *
+ * Assert.isNull(value, () -> "The value '" + value + "' must be null");
+ *
+ * @param object the object to check
+ * @param messageSupplier a supplier for the exception message to use if the
+ * assertion fails
+ * @throws IllegalArgumentException if the object is not {@code null}
+ * @since 5.0
+ */
+ public static void isNull( Object object, Supplier messageSupplier) {
+ if (object != null) {
+ throw new IllegalArgumentException(nullSafeGet(messageSupplier));
+ }
+ }
+
+
+
+ /**
+ * Assert that an object is not {@code null}.
+ *
Assert.notNull(clazz, "The class must not be null");
+ * @param object the object to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object is {@code null}
+ */
+ public static void notNull( Object object, String message) {
+ if (object == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Assert that an object is not {@code null}.
+ *
+ * Assert.notNull(clazz, () -> "The class '" + clazz.getName() + "' must not be null");
+ *
+ * @param object the object to check
+ * @param messageSupplier a supplier for the exception message to use if the
+ * assertion fails
+ * @throws IllegalArgumentException if the object is {@code null}
+ * @since 5.0
+ */
+ public static void notNull( Object object, Supplier messageSupplier) {
+ if (object == null) {
+ throw new IllegalArgumentException(nullSafeGet(messageSupplier));
+ }
+ }
+
+
+
+
+ /**
+ * Assert that an array contains no {@code null} elements.
+ *
Note: Does not complain if the array is empty!
+ *
Assert.noNullElements(array, "The array must contain non-null elements");
+ * @param array the array to check
+ * @param message the exception message to use if the assertion fails
+ * @throws IllegalArgumentException if the object array contains a {@code null} element
+ */
+ public static void noNullElements( Object[] array, String message) {
+ if (array != null) {
+ for (Object element : array) {
+ if (element == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+ }
+ }
+
+ /**
+ * Assert that an array contains no {@code null} elements.
+ *
Note: Does not complain if the array is empty!
+ *
+ * Assert.noNullElements(array, () -> "The " + arrayType + " array must contain non-null elements");
+ *
+ * @param array the array to check
+ * @param messageSupplier a supplier for the exception message to use if the
+ * assertion fails
+ * @throws IllegalArgumentException if the object array contains a {@code null} element
+ * @since 5.0
+ */
+ public static void noNullElements( Object[] array, Supplier messageSupplier) {
+ if (array != null) {
+ for (Object element : array) {
+ if (element == null) {
+ throw new IllegalArgumentException(nullSafeGet(messageSupplier));
+ }
+ }
+ }
+ }
+
+
+
+ private static String nullSafeGet( Supplier messageSupplier) {
+ return (messageSupplier != null ? messageSupplier.get() : null);
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/CharSequenceUtils.java b/common-util/src/main/java/com/vonchange/common/util/CharSequenceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5fa8b7162ca06da9668172dc53200ac5ab84491
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/CharSequenceUtils.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.vonchange.common.util;
+
+/**
+ *
Operations on {@link CharSequence} that are
+ * {@code null} safe.
Returns a new {@code CharSequence} that is a subsequence of this
+ * sequence starting with the {@code char} value at the specified index.
+ *
+ *
This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
+ * The length (in {@code char}) of the returned sequence is {@code length() - start},
+ * so if {@code start == end} then an empty sequence is returned.
+ *
+ * @param cs the specified subsequence, null returns null
+ * @param start the start index, inclusive, valid
+ * @return a new subsequence, may be null
+ * @throws IndexOutOfBoundsException if {@code start} is negative or if
+ * {@code start} is greater than {@code length()}
+ */
+ public static CharSequence subSequence(final CharSequence cs, final int start) {
+ return cs == null ? null : cs.subSequence(start, cs.length());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ *
Finds the first index in the {@code CharSequence} that matches the
+ * specified character.
+ *
+ * @param cs the {@code CharSequence} to be processed, not null
+ * @param searchChar the char to be searched for
+ * @param start the start index, negative starts at the string start
+ * @return the index where the search char was found, -1 if not found
+ */
+ static int indexOf(final CharSequence cs, final int searchChar, int start) {
+ if (cs instanceof String) {
+ return ((String) cs).indexOf(searchChar, start);
+ }
+ final int sz = cs.length();
+ if (start < 0) {
+ start = 0;
+ }
+ for (int i = start; i < sz; i++) {
+ if (cs.charAt(i) == searchChar) {
+ return i;
+ }
+ }
+ return NOT_FOUND;
+ }
+
+ /**
+ * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
+ *
+ * @param cs the {@code CharSequence} to be processed
+ * @param searchChar the {@code CharSequence} to be searched for
+ * @param start the start index
+ * @return the index where the search sequence was found
+ */
+ static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
+ return cs.toString().indexOf(searchChar.toString(), start);
+ }
+
+ /**
+ *
Finds the last index in the {@code CharSequence} that matches the
+ * specified character.
+ *
+ * @param cs the {@code CharSequence} to be processed
+ * @param searchChar the char to be searched for
+ * @param start the start index, negative returns -1, beyond length starts at end
+ * @return the index where the search char was found, -1 if not found
+ */
+ static int lastIndexOf(final CharSequence cs, final int searchChar, int start) {
+ if (cs instanceof String) {
+ return ((String) cs).lastIndexOf(searchChar, start);
+ }
+ final int sz = cs.length();
+ if (start < 0) {
+ return NOT_FOUND;
+ }
+ if (start >= sz) {
+ start = sz - 1;
+ }
+ for (int i = start; i >= 0; --i) {
+ if (cs.charAt(i) == searchChar) {
+ return i;
+ }
+ }
+ return NOT_FOUND;
+ }
+
+ /**
+ * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
+ *
+ * @param cs the {@code CharSequence} to be processed
+ * @param searchChar the {@code CharSequence} to be searched for
+ * @param start the start index
+ * @return the index where the search sequence was found
+ */
+ static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
+ return cs.toString().lastIndexOf(searchChar.toString(), start);
+ }
+
+ /**
+ * Green implementation of toCharArray.
+ *
+ * @param cs the {@code CharSequence} to be processed
+ * @return the resulting char array
+ */
+ static char[] toCharArray(final CharSequence cs) {
+ if (cs instanceof String) {
+ return ((String) cs).toCharArray();
+ }
+ final int sz = cs.length();
+ final char[] array = new char[cs.length()];
+ for (int i = 0; i < sz; i++) {
+ array[i] = cs.charAt(i);
+ }
+ return array;
+ }
+
+ /**
+ * Green implementation of regionMatches.
+ *
+ * @param cs the {@code CharSequence} to be processed
+ * @param ignoreCase whether or not to be case insensitive
+ * @param thisStart the index to start on the {@code cs} CharSequence
+ * @param substring the {@code CharSequence} to be looked for
+ * @param start the index to start on the {@code substring} CharSequence
+ * @param length character length of the region
+ * @return whether the region matched
+ */
+ static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart,
+ final CharSequence substring, final int start, final int length) {
+ if (cs instanceof String && substring instanceof String) {
+ return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
+ }
+ int index1 = thisStart;
+ int index2 = start;
+ int tmpLen = length;
+
+ while (tmpLen-- > 0) {
+ final char c1 = cs.charAt(index1++);
+ final char c2 = substring.charAt(index2++);
+
+ if (c1 == c2) {
+ continue;
+ }
+
+ if (!ignoreCase) {
+ return false;
+ }
+
+ // The same check as in String.regionMatches():
+ if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
+ && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/CharUtil.java b/common-util/src/main/java/com/vonchange/common/util/CharUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..54453b076dba2a72553d22f468c8c865fc4e5a98
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/CharUtil.java
@@ -0,0 +1,402 @@
+package com.vonchange.common.util;
+
+public class CharUtil {
+
+ // ---------------------------------------------------------------- simple
+
+ /**
+ * Converts (signed) byte to (unsigned) char.
+ */
+ public static char toChar(final byte b) {
+ return (char) (b & 0xFF);
+ }
+
+ /**
+ * Converts char array into byte array by stripping the high byte of each character.
+ */
+ public static byte[] toSimpleByteArray(final char[] carr) {
+ byte[] barr = new byte[carr.length];
+ for (int i = 0; i < carr.length; i++) {
+ barr[i] = (byte) carr[i];
+ }
+ return barr;
+ }
+
+ /**
+ * Converts char sequence into byte array.
+ * @see #toSimpleByteArray(char[])
+ */
+ public static byte[] toSimpleByteArray(final CharSequence charSequence) {
+ byte[] barr = new byte[charSequence.length()];
+ for (int i = 0; i < barr.length; i++) {
+ barr[i] = (byte) charSequence.charAt(i);
+ }
+ return barr;
+ }
+
+ /**
+ * Converts byte array to char array by simply extending bytes to chars.
+ */
+ public static char[] toSimpleCharArray(final byte[] barr) {
+ char[] carr = new char[barr.length];
+ for (int i = 0; i < barr.length; i++) {
+ carr[i] = (char) (barr[i] & 0xFF);
+ }
+ return carr;
+ }
+
+ // ---------------------------------------------------------------- ascii
+
+ /**
+ * Returns ASCII value of a char. In case of overload, 0x3F is returned.
+ */
+ public static int toAscii(final char c) {
+ if (c <= 0xFF) {
+ return c;
+ } else {
+ return 0x3F;
+ }
+ }
+
+ /**
+ * Converts char array into {@link #toAscii(char) ASCII} array.
+ */
+ public static byte[] toAsciiByteArray(final char[] carr) {
+ byte[] barr = new byte[carr.length];
+ for (int i = 0; i < carr.length; i++) {
+ barr[i] = (byte) ((int) (carr[i] <= 0xFF ? carr[i] : 0x3F));
+ }
+ return barr;
+ }
+
+ /**
+ * Converts char sequence into ASCII byte array.
+ */
+ public static byte[] toAsciiByteArray(final CharSequence charSequence) {
+ byte[] barr = new byte[charSequence.length()];
+ for (int i = 0; i < barr.length; i++) {
+ char c = charSequence.charAt(i);
+ barr[i] = (byte) ((int) (c <= 0xFF ? c : 0x3F));
+ }
+ return barr;
+ }
+
+ // ---------------------------------------------------------------- raw arrays
+
+ /**
+ * Converts char array into byte array by replacing each character with two bytes.
+ */
+ public static byte[] toRawByteArray(final char[] carr) {
+ byte[] barr = new byte[carr.length << 1];
+ for (int i = 0, bpos = 0; i < carr.length; i++) {
+ char c = carr[i];
+ barr[bpos++] = (byte) ((c & 0xFF00) >> 8);
+ barr[bpos++] = (byte) (c & 0x00FF);
+ }
+ return barr;
+ }
+
+ public static char[] toRawCharArray(final byte[] barr) {
+ int carrLen = barr.length >> 1;
+ if (carrLen << 1 < barr.length) {
+ carrLen++;
+ }
+ char[] carr = new char[carrLen];
+ int i = 0, j = 0;
+ while (i < barr.length) {
+ char c = (char) (barr[i] << 8);
+ i++;
+
+ if (i != barr.length) {
+ c += barr[i] & 0xFF;
+ i++;
+ }
+ carr[j++] = c;
+ }
+ return carr;
+ }
+
+
+
+ /**
+ * Match if one character equals to any of the given character.
+ *
+ * @return true if characters match any character from given array,
+ * otherwise false
+ */
+ public static boolean equalsOne(final char c, final char[] match) {
+ for (char aMatch : match) {
+ if (c == aMatch) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Finds index of the first character in given array the matches any from the
+ * given set of characters.
+ *
+ * @return index of matched character or -1
+ */
+ public static int findFirstEqual(final char[] source, final int index, final char[] match) {
+ for (int i = index; i < source.length; i++) {
+ if (equalsOne(source[i], match)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Finds index of the first character in given array the matches any from the
+ * given set of characters.
+ *
+ * @return index of matched character or -1
+ */
+ public static int findFirstEqual(final char[] source, final int index, final char match) {
+ for (int i = index; i < source.length; i++) {
+ if (source[i] == match) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * Finds index of the first character in given array the differs from the
+ * given set of characters.
+ *
+ * @return index of matched character or -1
+ */
+ public static int findFirstDiff(final char[] source, final int index, final char[] match) {
+ for (int i = index; i < source.length; i++) {
+ if (!equalsOne(source[i], match)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Finds index of the first character in given array the differs from the
+ * given set of characters.
+ *
+ * @return index of matched character or -1
+ */
+ public static int findFirstDiff(final char[] source, final int index, final char match) {
+ for (int i = index; i < source.length; i++) {
+ if (source[i] != match) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ // ---------------------------------------------------------------- is
+
+ /**
+ * Returns true if character is a white space ({@code <= ' '}).
+ * White space definition is taken from String class (see: trim()).
+ * This method has different results then Character#isWhitespace."
+ */
+ public static boolean isWhitespace(final char c) {
+ return c <= ' ';
+ }
+
+ /**
+ * Returns true if specified character is lowercase ASCII.
+ * If user uses only ASCIIs, it is much much faster.
+ */
+ public static boolean isLowercaseAlpha(final char c) {
+ return (c >= 'a') && (c <= 'z');
+ }
+
+ /**
+ * Returns true if specified character is uppercase ASCII.
+ * If user uses only ASCIIs, it is much much faster.
+ */
+ public static boolean isUppercaseAlpha(final char c) {
+ return (c >= 'A') && (c <= 'Z');
+ }
+
+ public static boolean isAlphaOrDigit(final char c) {
+ return isDigit(c) || isAlpha(c);
+ }
+
+ public static boolean isWordChar(final char c) {
+ return isDigit(c) || isAlpha(c) || (c == '_');
+ }
+
+ public static boolean isPropertyNameChar(final char c) {
+ return isDigit(c) || isAlpha(c) || (c == '_') || (c == '.') || (c == '[') || (c == ']');
+ }
+
+ // ---------------------------------------------------------------- RFC
+
+ /**
+ * Indicates whether the given character is in the {@code ALPHA} set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isAlpha(final char c) {
+ return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
+ }
+
+ /**
+ * Indicates whether the given character is in the {@code DIGIT} set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isDigit(final char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ /**
+ * Indicates whether the given character is the hexadecimal digit.
+ */
+ public static boolean isHexDigit(final char c) {
+ return (c >= '0' && c <= '9') || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'));
+ }
+
+ /**
+ * Indicates whether the given character is in the gen-delims set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isGenericDelimiter(final int c) {
+ switch (c) {
+ case ':':
+ case '/':
+ case '?':
+ case '#':
+ case '[':
+ case ']':
+ case '@':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Indicates whether the given character is in the sub-delims set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isSubDelimiter(final int c) {
+ switch (c) {
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Indicates whether the given character is in the reserved set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isReserved(final char c) {
+ return isGenericDelimiter(c) || isSubDelimiter(c);
+ }
+
+ /**
+ * Indicates whether the given character is in the unreserved set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isUnreserved(final char c) {
+ return isAlpha(c) || isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
+ }
+
+ /**
+ * Indicates whether the given character is in the pchar set.
+ *
+ * @see RFC 3986, appendix A
+ */
+ public static boolean isPchar(final char c) {
+ return isUnreserved(c) || isSubDelimiter(c) || c == ':' || c == '@';
+ }
+
+
+ // ---------------------------------------------------------------- conversions
+
+ /**
+ * Uppers lowercase ASCII char.
+ */
+ public static char toUpperAscii(char c) {
+ if (isLowercaseAlpha(c)) {
+ c -= (char) 0x20;
+ }
+ return c;
+ }
+
+
+ /**
+ * Lowers uppercase ASCII char.
+ */
+ public static char toLowerAscii(char c) {
+ if (isUppercaseAlpha(c)) {
+ c += (char) 0x20;
+ }
+ return c;
+ }
+
+ /**
+ * Converts hex char to int value.
+ */
+ public static int hex2int(final char c) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return c - '0';
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ return c - 55;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ return c - 87;
+ default:
+ throw new IllegalArgumentException("Not a hex: " + c);
+ }
+ }
+
+ /**
+ * Converts integer digit to heck char.
+ */
+ public static char int2hex(final int i) {
+ return HEX_CHARS[i];
+ }
+
+ public static final char[] HEX_CHARS = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/ClazzUtils.java b/common-util/src/main/java/com/vonchange/common/util/ClazzUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..68f72aa44facb65f11f4a3944994b868271c1188
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/ClazzUtils.java
@@ -0,0 +1,18 @@
+package com.vonchange.common.util;
+
+import com.vonchange.common.util.bean.convert.Converter;
+
+public class ClazzUtils {
+ private ClazzUtils() {
+ throw new IllegalStateException("Utility class");
+ }
+ /**
+ * isBaseType
+ */
+ public static boolean isBaseType(Class> clazz) {
+ //||clazz== Time.class||clazz == Timestamp.class
+ return Converter.hasConvertKey(clazz)|| clazz.isPrimitive()||clazz.isEnum();
+ }
+
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/ConvertUtil.java b/common-util/src/main/java/com/vonchange/common/util/ConvertUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..d911207578238a5c626a1cb35da616a07eb16cc4
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/ConvertUtil.java
@@ -0,0 +1,182 @@
+package com.vonchange.common.util;
+
+
+import com.vonchange.common.util.bean.convert.Converter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Date;
+
+/**
+ * 简单方式(效率高)实现类型转换,细节部分会不如ConvertUtils :ConvertUtils一次类型转换需要69ms 有点代价过高
+ * 不过多个也大概是这个时间?
+ *
+ * @author vonchange@163.com
+ */
+public class ConvertUtil {
+ private static final String NULLSTR = "NULL";
+
+ private static Object toNull(Object value) {
+ if (null == value) {
+ return null;
+ }
+ if (value instanceof String) {
+ value = value.toString().trim();
+ if (NULLSTR.equals(value)) {
+ return null;
+ }
+ if("".equals(value)){
+ return null;
+ }
+ }
+ return value;
+ }
+
+ public static Integer toInteger(Object value) {
+ value = toNull(value);
+ return Converter.get().toInteger(value);
+ }
+
+ public static String toString(Object value) {
+ if (null == value) {
+ return null;
+ }
+ return Converter.get().toString(value);
+ }
+
+ public static Long toLong(Object value) {
+ value = toNull(value);
+ return Converter.get().toLong(value);
+ }
+
+ public static Boolean toBoolean(Object value) {
+ value = toNull(value);
+ return Converter.get().toBoolean(value);
+ }
+
+ public static Double toDouble(Object value) {
+ value = toNull(value);
+ return Converter.get().toDouble(value);
+ }
+
+ public static Float toFloat(Object value) {
+ value = toNull(value);
+ return Converter.get().toFloat(value);
+ }
+
+ public static Short toShort(Object value) {
+ value = toNull(value);
+ return Converter.get().toShort(value);
+ }
+
+ public static Byte toByte(Object value) {
+ value = toNull(value);
+ return Converter.get().toByte(value);
+ }
+ public static Character toCharacter(Object value){
+ value = toNull(value);
+ return Converter.get().toCharacter(value);
+ }
+ public static BigInteger toBigInteger(Object value){
+ value = toNull(value);
+ return Converter.get().toBigInteger(value);
+ }
+
+
+
+ public static BigDecimal toBigDecimal(Object value) {
+ value = toNull(value);
+ return Converter.get().toBigDecimal(value);
+ }
+
+ public static LocalDateTime toLocalDateTime(Object value) {
+ value = toNull(value);
+ return Converter.get().toLocalDateTime(value);
+ }
+ public static LocalDate toLocalDate(Object value) {
+ value = toNull(value);
+ return Converter.get().toLocalDate(value);
+ }
+
+ public static LocalTime toLocalTime(Object value) {
+ value = toNull(value);
+ return Converter.get().toLocalTime(value);
+ }
+
+ public static Date toDate(Object value) {
+ value = toNull(value);
+ return Converter.get().toDate(value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T toObject(Object value, Class> targetType) {
+ if (null == value) {
+ return null;
+ }
+ if(value.getClass().isAssignableFrom(targetType)){
+ return (T) value;
+ }
+ return toObjectDetail(value,targetType);
+ }
+ @SuppressWarnings("unchecked")
+ public static T toObjectDetail(Object value, Class> targetType) {
+ if (null == value) {
+ return null;
+ }
+
+ if (targetType.isAssignableFrom(String.class)) {
+ return (T) ConvertUtil.toString(value);
+ }
+ if (targetType.isAssignableFrom(Integer.class)||targetType.isAssignableFrom(int.class)) {
+ return (T)ConvertUtil.toInteger(value);
+ }
+ if (targetType.isAssignableFrom(Long.class)||targetType.isAssignableFrom(long.class)) {
+ return (T)ConvertUtil.toLong(value);
+ }
+ if (targetType.isAssignableFrom(BigDecimal.class)) {
+ return (T)ConvertUtil.toBigDecimal(value);
+ }
+ if (targetType.isAssignableFrom(Boolean.class)||targetType.isAssignableFrom(boolean.class)) {
+ return (T)ConvertUtil.toBoolean(value);
+ }
+ if (targetType.isAssignableFrom(Float.class)||targetType.isAssignableFrom(float.class)) {
+ return (T)ConvertUtil.toFloat(value);
+ }
+ if (targetType.isAssignableFrom(Double.class)||targetType.isAssignableFrom(double.class)) {
+ return (T)ConvertUtil.toDouble(value);
+ }
+ if (targetType.isAssignableFrom(Short.class)||targetType.isAssignableFrom(short.class)) {
+ return (T)ConvertUtil.toShort(value);
+ }
+ if (targetType.isAssignableFrom(Byte.class)||targetType.isAssignableFrom(byte.class)) {
+ return (T)ConvertUtil.toByte(value);
+ }
+ if (targetType.isAssignableFrom(Character.class)||targetType.isAssignableFrom(char.class)) {
+ return (T)ConvertUtil.toCharacter(value);
+ }
+ if (targetType.isAssignableFrom(BigInteger.class)) {
+ return (T)ConvertUtil.toBigInteger(value);
+ }
+ if (targetType.isAssignableFrom(Date.class)) {
+ return (T)ConvertUtil.toDate(value);
+ }
+ if (targetType.isAssignableFrom(LocalDateTime.class)) {
+ return (T)ConvertUtil.toLocalDateTime(value);
+ }
+ if (targetType.isAssignableFrom(LocalDate.class)) {
+ return (T)ConvertUtil.toLocalDate(value);
+ }
+ if (targetType.isAssignableFrom(LocalTime.class)) {
+ return (T)ConvertUtil.toLocalTime(value);
+ }
+ if (targetType.isEnum()) {
+ Class type = targetType;
+ return (T) Enum.valueOf(type, value.toString());
+ }
+ return (T)value;
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/FileUtil.java b/common-util/src/main/java/com/vonchange/common/util/FileUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..7095d459477e7161e0c2a5e65cd3867e58a8d919
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/FileUtil.java
@@ -0,0 +1,26 @@
+package com.vonchange.common.util;
+
+
+import com.vonchange.common.util.io.UnicodeInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class FileUtil {
+ public static String readUTFString(final InputStream inputStream) throws IOException {
+ UnicodeInputStream in = null;
+ try {
+ in = new UnicodeInputStream(inputStream, null);
+ return StreamUtil.copy(in, detectEncoding(in)).toString();
+ } finally {
+ StreamUtil.close(in);
+ }
+ }
+ private static String detectEncoding(final UnicodeInputStream in) {
+ String encoding = in.getDetectedEncoding();
+ if (encoding == null) {
+ encoding = StringPool.UTF_8;
+ }
+ return encoding;
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/JsonUtil.java b/common-util/src/main/java/com/vonchange/common/util/JsonUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..c917b2d8c9f4ff01795d4cbf412bba3b9dcb7d21
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/JsonUtil.java
@@ -0,0 +1,72 @@
+package com.vonchange.common.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class JsonUtil {
+ private static Logger logger = LoggerFactory.getLogger(JsonUtil.class);
+ private JsonUtil() {
+ throw new IllegalStateException("Utility class");
+ }
+ public static String toJson(Object object) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ return objectMapper.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ logger.error("序列化对象失败{}",e);
+ }
+ return null;
+ }
+
+ public static T fromJson(String json, TypeReference type) {
+ return fromJson(json,null,type);
+ }
+ public static T evalJson(String json, TypeReference type) throws IOException {
+ return evalJson(json,null,type);
+ }
+ public static T evalJson(String json, Class clazz) throws IOException {
+ return evalJson(json,clazz,null);
+ }
+ public static T evalJson(String json, Class clazz,TypeReference type) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
+ T t =null;
+ if(null!=clazz){
+ t = mapper.readValue(json, clazz);
+ }
+ if(null!=type){
+ t = mapper.readValue(json, type);
+ }
+ return t;
+ }
+
+ private static T fromJson(String json, Class clazz,TypeReference type) {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
+ T t =null;
+ try {
+ if(null!=clazz){
+ t = mapper.readValue(json, clazz);
+ }
+ if(null!=type){
+ t = mapper.readValue(json, type);
+ }
+ } catch (IOException e) {
+ logger.error("反序列化对象失败{}",e);
+ }
+ return t;
+ }
+ public static T fromJson(String json, Class clazz) {
+ return fromJson(json,clazz,null);
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/MD5Util.java b/common-util/src/main/java/com/vonchange/common/util/MD5Util.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8ee4f6809d5e6ac2bc5438f259db988fa08db22
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/MD5Util.java
@@ -0,0 +1,36 @@
+package com.vonchange.common.util;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class MD5Util {
+ public static String getMD5(String str){
+ return getMD5(str.getBytes());
+ }
+ public static String getMD5(byte[] source){
+ String s=null;
+ //用来将字节转换成16进制表示的字符
+ char[] hexDigits={'0','1','2','3','4','5','6','7','8','9',
+ 'a','b','c','d','e','f'};
+ try {
+ MessageDigest md=MessageDigest.getInstance("MD5");
+ md.update(source);
+ //MD5的计算结果是一个128位的长整数,用字节表示为16个字节
+ byte[] tmp=md.digest();
+ //每个字节用16进制表示的话,使用2个字符(高4位一个,低4位一个),所以表示成16进制需要32个字符
+ char[] str=new char[16*2];
+ int k=0;//转换结果中对应的字符位置
+ for(int i=0;i<16;i++){//对MD5的每一个字节转换成16进制字符
+ byte byte0=tmp[i];
+ str[k++]=hexDigits[byte0>>>4 & 0xf];//对字节高4位进行16进制转换
+ str[k++]=hexDigits[byte0 & 0xf]; //对字节低4位进行16进制转换
+ }
+ s=new String(str);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return s;
+ }
+
+
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/NestedExceptionUtils.java b/common-util/src/main/java/com/vonchange/common/util/NestedExceptionUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..00b1e1e412fdf49465bd77f789fb42dd87699f50
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/NestedExceptionUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.vonchange.common.util;
+
+public abstract class NestedExceptionUtils {
+
+ /**
+ * Build a message for the given base message and root cause.
+ * @param message the base message
+ * @param cause the root cause
+ * @return the full exception message
+ */
+
+ public static String buildMessage( String message, Throwable cause) {
+ if (cause == null) {
+ return message;
+ }
+ StringBuilder sb = new StringBuilder(64);
+ if (message != null) {
+ sb.append(message).append("; ");
+ }
+ sb.append("nested exception is ").append(cause);
+ return sb.toString();
+ }
+
+ /**
+ * Retrieve the innermost cause of the given exception, if any.
+ * @param original the original exception to introspect
+ * @return the innermost exception, or {@code null} if none
+ * @since 4.3.9
+ */
+
+ public static Throwable getRootCause( Throwable original) {
+ if (original == null) {
+ return null;
+ }
+ Throwable rootCause = null;
+ Throwable cause = original.getCause();
+ while (cause != null && cause != rootCause) {
+ rootCause = cause;
+ cause = cause.getCause();
+ }
+ return rootCause;
+ }
+
+ /**
+ * Retrieve the most specific cause of the given exception, that is,
+ * either the innermost cause (root cause) or the exception itself.
+ *
Differs from {@link #getRootCause} in that it falls back
+ * to the original exception if there is no root cause.
+ * @param original the original exception to introspect
+ * @return the most specific cause (never {@code null})
+ * @since 4.3.9
+ */
+ public static Throwable getMostSpecificCause(Throwable original) {
+ Throwable rootCause = getRootCause(original);
+ return (rootCause != null ? rootCause : original);
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/StreamUtil.java b/common-util/src/main/java/com/vonchange/common/util/StreamUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d26a798c591920e346981e930f919bfd93158d1
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/StreamUtil.java
@@ -0,0 +1,144 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util;
+
+
+import com.vonchange.common.util.io.FastCharArrayWriter;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+
+/**
+ * Optimized byte and character stream utilities.
+ */
+public class StreamUtil {
+ private static final int ZERO = 0;
+ private static final int NEGATIVE_ONE = -1;
+ private static final int ALL = -1;
+ public static int ioBufferSizeX = 16384;
+ /**
+ * Closes silently the closable object. If it is {@link Flushable}, it
+ * will be flushed first. No exception will be thrown if an I/O error occurs.
+ */
+ public static void close(final Closeable closeable) {
+ if (closeable != null) {
+ if (closeable instanceof Flushable) {
+ try {
+ ((Flushable) closeable).flush();
+ } catch (IOException ignored) {
+ }
+ }
+
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+
+ public static FastCharArrayWriter copy(final InputStream input, final String encoding) throws IOException {
+ return copy(input, encoding, ALL);
+ }
+ public static FastCharArrayWriter copy(final InputStream input, final String encoding, final int count) throws IOException {
+ try (FastCharArrayWriter output = createFastCharArrayWriter()) {
+ copy(input, output, encoding, count);
+ return output;
+ }
+ }
+ private static FastCharArrayWriter createFastCharArrayWriter() {
+ return new FastCharArrayWriter(bufferSize());
+ }
+
+ private static int bufferSize() {
+ return ioBufferSizeX;
+ }
+ private static int bufferSize(final int count) {
+ final int ioBufferSize = ioBufferSizeX;
+ if (count < ioBufferSize) {
+ return count;
+ } else {
+ return ioBufferSize;
+ }
+ }
+ public static T copy(final InputStream input, final T output, final String encoding, final int count) throws IOException {
+ copy(inputStreamReadeOf(input, encoding), output, count);
+ return output;
+ }
+ public static InputStreamReader inputStreamReadeOf(final InputStream input, final String encoding) throws UnsupportedEncodingException {
+ return new InputStreamReader(input, encoding);
+ }
+ public static int copy(final Reader input, final Writer output, final int count) throws IOException {
+ if (count == ALL) {
+ return copy(input, output);
+ }
+
+ int numToRead = count;
+ char[] buffer = new char[numToRead];
+
+ int totalRead = ZERO;
+ int read;
+
+ while (numToRead > ZERO) {
+ read = input.read(buffer, ZERO, bufferSize(numToRead));
+ if (read == NEGATIVE_ONE) {
+ break;
+ }
+ output.write(buffer, ZERO, read);
+
+ numToRead = numToRead - read;
+ totalRead = totalRead + read;
+ }
+
+ output.flush();
+ return totalRead;
+ }
+ public static int copy(final Reader input, final Writer output) throws IOException {
+ int numToRead = bufferSize();
+ char[] buffer = new char[numToRead];
+
+ int totalRead = ZERO;
+ int read;
+
+ while ((read = input.read(buffer, ZERO, numToRead)) >= ZERO) {
+ output.write(buffer, ZERO, read);
+ totalRead = totalRead + read;
+ }
+
+ output.flush();
+ return totalRead;
+ }
+
+
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/StringPool.java b/common-util/src/main/java/com/vonchange/common/util/StringPool.java
new file mode 100644
index 0000000000000000000000000000000000000000..968b4a061a34a9e3dcba72beec114e4ffaa69690
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/StringPool.java
@@ -0,0 +1,74 @@
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package com.vonchange.common.util;
+
+public interface StringPool {
+ String AMPERSAND = "&";
+ String AND = "and";
+ String AT = "@";
+ String ASTERISK = "*";
+ String STAR = "*";
+ String BACK_SLASH = "\\";
+ String COLON = ":";
+ String COMMA = ",";
+ String DASH = "-";
+ String DOLLAR = "$";
+ String DOT = ".";
+ String DOTDOT = "..";
+ String DOT_CLASS = ".class";
+ String DOT_JAVA = ".java";
+ String EMPTY = "";
+ String EQUALS = "=";
+ String FALSE = "false";
+ String SLASH = "/";
+ String HASH = "#";
+ String HAT = "^";
+ String LEFT_BRACE = "{";
+ String LEFT_BRACKET = "(";
+ String LEFT_CHEV = "<";
+ String NEWLINE = "\n";
+ String N = "n";
+ String NO = "no";
+ String NULL = "null";
+ String OFF = "off";
+ String ON = "on";
+ String PERCENT = "%";
+ String PIPE = "|";
+ String PLUS = "+";
+ String QUESTION_MARK = "?";
+ String EXCLAMATION_MARK = "!";
+ String QUOTE = "\"";
+ String RETURN = "\r";
+ String TAB = "\t";
+ String RIGHT_BRACE = "}";
+ String RIGHT_BRACKET = ")";
+ String RIGHT_CHEV = ">";
+ String SEMICOLON = ";";
+ String SINGLE_QUOTE = "'";
+ String BACKTICK = "`";
+ String SPACE = " ";
+ String TILDA = "~";
+ String LEFT_SQ_BRACKET = "[";
+ String RIGHT_SQ_BRACKET = "]";
+ String TRUE = "true";
+ String UNDERSCORE = "_";
+ String UTF_8 = "UTF-8";
+ String US_ASCII = "US-ASCII";
+ String ISO_8859_1 = "ISO-8859-1";
+ String Y = "y";
+ String YES = "yes";
+ String ONE = "1";
+ String ZERO = "0";
+ String DOLLAR_LEFT_BRACE = "${";
+ String CRLF = "\r\n";
+ String HTML_NBSP = " ";
+ String HTML_AMP = "&";
+ String HTML_QUOTE = """;
+ String HTML_LT = "<";
+ String HTML_GT = ">";
+ String[] EMPTY_ARRAY = new String[0];
+ byte[] BYTES_NEW_LINE = "\n".getBytes();
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/StringUtils.java b/common-util/src/main/java/com/vonchange/common/util/StringUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..4704205c37efe79921cd815fa76df7b3a6aaabc2
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/StringUtils.java
@@ -0,0 +1,484 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.vonchange.common.util;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.vonchange.common.util.StringPool.EMPTY;
+
+public class StringUtils {
+ public static String uuid() {
+ String uuid = UUID.randomUUID().toString();
+ return remove(uuid, "-");
+ }
+ public static String remove(String s, String sub) {
+ int c = 0;
+ int sublen = sub.length();
+ if (sublen == 0) {
+ return s;
+ } else {
+ int i = s.indexOf(sub, c);
+ if (i == -1) {
+ return s;
+ } else {
+ StringBuilder sb = new StringBuilder(s.length());
+
+ do {
+ sb.append(s, c, i);
+ c = i + sublen;
+ } while((i = s.indexOf(sub, c)) != -1);
+
+ if (c < s.length()) {
+ sb.append(s, c, s.length());
+ }
+
+ return sb.toString();
+ }
+ }
+ }
+ public static boolean isBlank(final CharSequence cs) {
+ int strLen;
+ if (cs == null || (strLen = cs.length()) == 0) {
+ return true;
+ }
+ for (int i = 0; i < strLen; i++) {
+ if (Character.isWhitespace(cs.charAt(i)) == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isNotBlank(final CharSequence cs) {
+ return !isBlank(cs);
+ }
+ /**
+ * 简单模板实现 String str = "I'm not a {0}, age is {1,number,short}", height is
+ * {2,number,#.#};
+ *
+ * @param pattern
+ * @param arguments
+ * @return String
+ */
+ public static String format(String pattern, Object... arguments) {
+ MessageFormat temp = new MessageFormat(pattern);
+ return temp.format(arguments);
+ }
+ public static String strNums(String str, String split, int num) {
+ if(num==0){
+ return "";
+ }
+ StringBuilder fullStr = new StringBuilder();
+ for (int i = 0; i < num; i++) {
+ fullStr.append(str).append(split);
+ }
+ return fullStr.substring(0, fullStr.length()-split.length());
+ }
+ public static String strList(List strs, String split) {
+ if(null==strs||strs.isEmpty()){
+ return "";
+ }
+ StringBuilder fullStr = new StringBuilder();
+ for (String string : strs) {
+ fullStr.append(string).append(split);
+ }
+ return fullStr.substring(0, fullStr.length()-split.length());
+ }
+ public static Boolean isNull(Object obj) {
+ if (null == obj) {
+ return true;
+ }
+ String str = String.valueOf(obj);
+ if (isBlank(str)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static final char UNDERLINE='_';
+ public static String camelToUnderline(String param){
+ if (param==null||"".equals(param.trim())){
+ return "";
+ }
+ int len=param.length();
+ StringBuilder sb=new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ char c=param.charAt(i);
+ if (Character.isUpperCase(c)){
+ sb.append(UNDERLINE);
+ sb.append(Character.toLowerCase(c));
+ }else{
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 正则表达式 实现
+ * @param tplStr
+ * @param data
+ */
+ public static String tplByMap(String tplStr, Map data) {
+ Matcher m = Pattern.compile("\\$\\{([\\w\\.]*)\\}").matcher(tplStr);
+ List patternList = new ArrayList();
+ List replaceStrList = new ArrayList();
+ while (m.find()) {
+ String group = m.group();
+ patternList.add(group);
+ group = group.replaceAll("\\$|\\{|\\}", "");
+ String value = "";
+ if (null != data.get(group)) {
+ value = String.valueOf(data.get(group));
+ }
+ replaceStrList.add(value);
+ }
+ tplStr= replaceEach(tplStr, patternList.toArray(new String[patternList.size()]), replaceStrList.toArray(new String[replaceStrList.size()]));
+ return tplStr;
+ }
+
+ /*public static String tpl(String tplStr, Map data) {
+ StringTemplateParser stp = new StringTemplateParser();
+
+ return stp.parse(tplStr,macroName -> {
+ return ConvertUtil.toString(data.get(macroName));
+ });
+ }*/
+
+ public static String capitalize(final String str) {
+ int strLen;
+ if (str == null || (strLen = str.length()) == 0) {
+ return str;
+ }
+
+ final char firstChar = str.charAt(0);
+ if (Character.isTitleCase(firstChar)) {
+ // already capitalized
+ return str;
+ }
+
+ return new StringBuilder(strLen)
+ .append(Character.toTitleCase(firstChar))
+ .append(str.substring(1))
+ .toString();
+ }
+ public static boolean equals(final CharSequence cs1, final CharSequence cs2) {
+ if (cs1 == cs2) {
+ return true;
+ }
+ if (cs1 == null || cs2 == null) {
+ return false;
+ }
+ if (cs1 instanceof String && cs2 instanceof String) {
+ return cs1.equals(cs2);
+ }
+ return CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, Math.max(cs1.length(), cs2.length()));
+ }
+
+ public static String replaceEach(final String text, final String[] searchList, final String[] replacementList) {
+ return replaceEach(text, searchList, replacementList, false, 0);
+ }
+ private static String replaceEach(
+ final String text, final String[] searchList, final String[] replacementList, final boolean repeat, final int timeToLive) {
+
+ // mchyzer Performance note: This creates very few new objects (one major goal)
+ // let me know if there are performance requests, we can create a harness to measure
+
+ if (text == null || text.isEmpty() || searchList == null ||
+ searchList.length == 0 || replacementList == null || replacementList.length == 0) {
+ return text;
+ }
+
+ // if recursing, this shouldn't be less than 0
+ if (timeToLive < 0) {
+ throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
+ "output of one loop is the input of another");
+ }
+
+ final int searchLength = searchList.length;
+ final int replacementLength = replacementList.length;
+
+ // make sure lengths are ok, these need to be equal
+ if (searchLength != replacementLength) {
+ throw new IllegalArgumentException("Search and Replace array lengths don't match: "
+ + searchLength
+ + " vs "
+ + replacementLength);
+ }
+
+ // keep track of which still have matches
+ final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
+
+ // index on index that the match was found
+ int textIndex = -1;
+ int replaceIndex = -1;
+ int tempIndex = -1;
+
+ // index of replace array that will replace the search string found
+ // NOTE: logic duplicated below START
+ for (int i = 0; i < searchLength; i++) {
+ if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
+ searchList[i].isEmpty() || replacementList[i] == null) {
+ continue;
+ }
+ tempIndex = text.indexOf(searchList[i]);
+
+ // see if we need to keep searching for this
+ if (tempIndex == -1) {
+ noMoreMatchesForReplIndex[i] = true;
+ } else {
+ if (textIndex == -1 || tempIndex < textIndex) {
+ textIndex = tempIndex;
+ replaceIndex = i;
+ }
+ }
+ }
+ // NOTE: logic mostly below END
+
+ // no search strings found, we are done
+ if (textIndex == -1) {
+ return text;
+ }
+
+ int start = 0;
+
+ // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
+ int increase = 0;
+
+ // count the replacement text elements that are larger than their corresponding text being replaced
+ for (int i = 0; i < searchList.length; i++) {
+ if (searchList[i] == null || replacementList[i] == null) {
+ continue;
+ }
+ final int greater = replacementList[i].length() - searchList[i].length();
+ if (greater > 0) {
+ increase += 3 * greater; // assume 3 matches
+ }
+ }
+ // have upper-bound at 20% increase, then let Java take over
+ increase = Math.min(increase, text.length() / 5);
+
+ final StringBuilder buf = new StringBuilder(text.length() + increase);
+
+ while (textIndex != -1) {
+
+ for (int i = start; i < textIndex; i++) {
+ buf.append(text.charAt(i));
+ }
+ buf.append(replacementList[replaceIndex]);
+
+ start = textIndex + searchList[replaceIndex].length();
+
+ textIndex = -1;
+ replaceIndex = -1;
+ tempIndex = -1;
+ // find the next earliest match
+ // NOTE: logic mostly duplicated above START
+ for (int i = 0; i < searchLength; i++) {
+ if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
+ searchList[i].isEmpty() || replacementList[i] == null) {
+ continue;
+ }
+ tempIndex = text.indexOf(searchList[i], start);
+
+ // see if we need to keep searching for this
+ if (tempIndex == -1) {
+ noMoreMatchesForReplIndex[i] = true;
+ } else {
+ if (textIndex == -1 || tempIndex < textIndex) {
+ textIndex = tempIndex;
+ replaceIndex = i;
+ }
+ }
+ }
+ // NOTE: logic duplicated above END
+
+ }
+ final int textLength = text.length();
+ for (int i = start; i < textLength; i++) {
+ buf.append(text.charAt(i));
+ }
+ final String result = buf.toString();
+ if (!repeat) {
+ return result;
+ }
+
+ return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
+ }
+ public static boolean endsWith(final CharSequence str, final CharSequence suffix) {
+ return endsWith(str, suffix, false);
+ }
+ private static boolean endsWith(final CharSequence str, final CharSequence suffix, final boolean ignoreCase) {
+ if (str == null || suffix == null) {
+ return str == null && suffix == null;
+ }
+ if (suffix.length() > str.length()) {
+ return false;
+ }
+ final int strOffset = str.length() - suffix.length();
+ return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length());
+ }
+
+ public static boolean startsWithChar(String s, char c) {
+ if (s.length() == 0) {
+ return false;
+ } else {
+ return s.charAt(0) == c;
+ }
+ }
+ public static boolean containsOnlyDigitsAndSigns(CharSequence string) {
+ int size = string.length();
+
+ for(int i = 0; i < size; ++i) {
+ char c = string.charAt(i);
+ if (!isDigit(c) && c != '-' && c != '+') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ public static boolean isDigit(char c) {
+ return c >= '0' && c <= '9';
+ }
+ public static String[] split(final String src, final String delimiter) {
+ int maxparts = (src.length() / delimiter.length()) + 2; // one more for the last
+ int[] positions = new int[maxparts];
+ int dellen = delimiter.length();
+
+ int i, j = 0;
+ int count = 0;
+ positions[0] = - dellen;
+ while ((i = src.indexOf(delimiter, j)) != -1) {
+ count++;
+ positions[count] = i;
+ j = i + dellen;
+ }
+ count++;
+ positions[count] = src.length();
+
+ String[] result = new String[count];
+
+ for (i = 0; i < count; i++) {
+ result[i] = src.substring(positions[i] + dellen, positions[i + 1]);
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if string contains only digits.
+ */
+ public static boolean containsOnlyDigits(final CharSequence string) {
+ int size = string.length();
+ for (int i = 0; i < size; i++) {
+ char c = string.charAt(i);
+ if (!isDigit(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Splits a string in several parts (tokens) that are separated by delimiter
+ * characters. Delimiter may contains any number of character and it is
+ * always surrounded by two strings.
+ *
+ * @param src source to examine
+ * @param d string with delimiter characters
+ *
+ * @return array of tokens
+ */
+ public static String[] splitc(final String src, final String d) {
+ if ((d.length() == 0) || (src.length() == 0)) {
+ return new String[] {src};
+ }
+ return splitc(src, d.toCharArray());
+ }
+ /**
+ * Splits a string in several parts (tokens) that are separated by delimiter
+ * characters. Delimiter may contains any number of character and it is
+ * always surrounded by two strings.
+ *
+ * @param src source to examine
+ * @param delimiters char array with delimiter characters
+ *
+ * @return array of tokens
+ */
+ public static String[] splitc(final String src, final char[] delimiters) {
+ if ((delimiters.length == 0) || (src.length() == 0) ) {
+ return new String[] {src};
+ }
+ char[] srcc = src.toCharArray();
+
+ int maxparts = srcc.length + 1;
+ int[] start = new int[maxparts];
+ int[] end = new int[maxparts];
+
+ int count = 0;
+
+ start[0] = 0;
+ int s = 0, e;
+ if (CharUtil.equalsOne(srcc[0], delimiters)) { // string starts with delimiter
+ end[0] = 0;
+ count++;
+ s = CharUtil.findFirstDiff(srcc, 1, delimiters);
+ if (s == -1) { // nothing after delimiters
+ return new String[] {EMPTY, EMPTY};
+ }
+ start[1] = s; // new start
+ }
+ while (true) {
+ // find new end
+ e = CharUtil.findFirstEqual(srcc, s, delimiters);
+ if (e == -1) {
+ end[count] = srcc.length;
+ break;
+ }
+ end[count] = e;
+
+ // find new start
+ count++;
+ s = CharUtil.findFirstDiff(srcc, e, delimiters);
+ if (s == -1) {
+ start[count] = end[count] = srcc.length;
+ break;
+ }
+ start[count] = s;
+ }
+ count++;
+ String[] result = new String[count];
+ for (int i = 0; i < count; i++) {
+ result[i] = src.substring(start[i], end[i]);
+ }
+ return result;
+ }
+/* public static void main(String[] args) {
+
+ String template = "Hello ${foo}. Today is ${dayName}.";
+ // prepare data
+ Map map = new HashMap<>();
+ map.put("foo", "Jodd");
+ map.put("dayName", "Sunday");
+ }*/
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/BeanUtil.java b/common-util/src/main/java/com/vonchange/common/util/bean/BeanUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..75a6399f22825e5fa3d2a1b444b34a2bc2c2877f
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/BeanUtil.java
@@ -0,0 +1,45 @@
+
+package com.vonchange.common.util.bean;
+
+
+import com.vonchange.common.util.ConvertUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+public class BeanUtil {
+ private static final Logger log = LoggerFactory.getLogger(BeanUtil.class);
+ public Object getPropertyT(Object entity,String property) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
+ PropertyDescriptor propertyDescriptor = new PropertyDescriptor(property, entity.getClass());
+ return propertyDescriptor.getReadMethod().invoke(entity);
+ }
+ public void setPropertyT(Object entity,String property,Object value) throws InvocationTargetException, IllegalAccessException, IntrospectionException {
+ PropertyDescriptor propertyDescriptor = new PropertyDescriptor(property, entity.getClass());
+ value= ConvertUtil.toObject(value,propertyDescriptor.getPropertyType());
+ propertyDescriptor.getWriteMethod().invoke(entity,value);
+ }
+ public Object getProperty(Object entity,String property) {
+ try {
+ if(entity instanceof Map){
+ return ((Map) entity).get(property);
+ }
+ return getPropertyT(entity, property);
+ } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
+ log.error("",e);
+ return null;
+ }
+ }
+ public void setProperty(Object entity,String property,Object value){
+ try {
+ setPropertyT(entity, property, value);
+ } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
+ log.error("",e);
+ }
+ }
+
+}
+
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/Converter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/Converter.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ec01e55aaa994e982bcd154bf3a8be87c32fd8a
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/Converter.java
@@ -0,0 +1,563 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert;
+
+
+import com.vonchange.common.util.bean.convert.impl.BigDecimalConverter;
+import com.vonchange.common.util.bean.convert.impl.BigIntegerConverter;
+import com.vonchange.common.util.bean.convert.impl.BooleanConverter;
+import com.vonchange.common.util.bean.convert.impl.ByteArrayConverter;
+import com.vonchange.common.util.bean.convert.impl.ByteConverter;
+import com.vonchange.common.util.bean.convert.impl.CharacterConverter;
+import com.vonchange.common.util.bean.convert.impl.DateConverter;
+import com.vonchange.common.util.bean.convert.impl.DoubleConverter;
+import com.vonchange.common.util.bean.convert.impl.FloatConverter;
+import com.vonchange.common.util.bean.convert.impl.IntegerConverter;
+import com.vonchange.common.util.bean.convert.impl.LocalDateConverter;
+import com.vonchange.common.util.bean.convert.impl.LocalDateTimeConverter;
+import com.vonchange.common.util.bean.convert.impl.LocalTimeConverter;
+import com.vonchange.common.util.bean.convert.impl.LongConverter;
+import com.vonchange.common.util.bean.convert.impl.ShortConverter;
+import com.vonchange.common.util.bean.convert.impl.StringConverter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+public class Converter {
+
+ private static final Converter CONVERTER = new Converter();
+ private static Map,TypeConverter> convertMap= new HashMap<>();
+ static{
+ convertMap.put(Boolean.class,new BooleanConverter());
+ convertMap.put(BigDecimal.class,new BigDecimalConverter());
+ convertMap.put(BigInteger.class,new BigIntegerConverter());
+ convertMap.put(Byte.class,new ByteConverter());
+ convertMap.put(Character.class,new CharacterConverter());
+ convertMap.put(Double.class,new DoubleConverter());
+ convertMap.put(Float.class,new FloatConverter());
+ convertMap.put(Integer.class,new IntegerConverter());
+ convertMap.put(Long.class,new LongConverter());
+ convertMap.put(Short.class,new ShortConverter());
+ convertMap.put(String.class,new StringConverter());
+ convertMap.put(LocalDate.class,new LocalDateConverter());
+ convertMap.put(LocalDateTime.class,new LocalDateTimeConverter());
+ convertMap.put(LocalTime.class,new LocalTimeConverter());
+ convertMap.put(Date.class,new DateConverter());
+ convertMap.put(byte[].class,new ByteArrayConverter());
+ }
+
+ /**
+ * Returns default instance.
+ */
+ public static Converter get() {
+ return CONVERTER;
+ }
+
+ public static boolean hasConvertKey(Class> type){
+ return convertMap.containsKey(type);
+ }
+ @SuppressWarnings("unchecked")
+ public static TypeConverter getConvert(Class> type) {
+ /* for (Map.Entry,TypeConverter> entry:convertMap.entrySet()) {
+ if(entry.getKey().isAssignableFrom(type)){
+ return entry.getValue();
+ }
+ }*/
+ return convertMap.get(type);
+ }
+ // ---------------------------------------------------------------- boolean
+
+ /**
+ * Converts value to Boolean.
+ */
+ public Boolean toBoolean(final Object value) {
+ return (Boolean) getConvert(Boolean.class).convert(value);
+ }
+
+
+
+ /**
+ * Converts value to Boolean. Returns default value
+ * when conversion result is null
+ */
+ public Boolean toBoolean(final Object value, final Boolean defaultValue) {
+ final Boolean result = toBoolean(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to boolean. Returns default value
+ * when conversion result is null.
+ */
+ public boolean toBooleanValue(final Object value, final boolean defaultValue) {
+ final Boolean result = toBoolean(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.booleanValue();
+ }
+
+ /**
+ * Converts value to boolean with common default value.
+ */
+ public boolean toBooleanValue(final Object value) {
+ return toBooleanValue(value, false);
+ }
+
+ // ---------------------------------------------------------------- integer
+
+ /**
+ * Converts value to Integer.
+ */
+ public Integer toInteger(final Object value) {
+ return (Integer) getConvert(Integer.class).convert(value);
+ }
+
+ /**
+ * Converts value to Integer. Returns default value
+ * when conversion result is null
+ */
+ public Integer toInteger(final Object value, final Integer defaultValue) {
+ final Integer result = toInteger(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to int. Returns default value
+ * when conversion result is null.
+ */
+ public int toIntValue(final Object value, final int defaultValue) {
+ final Integer result = toInteger(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.intValue();
+ }
+
+ /**
+ * Converts value to int with common default value.
+ */
+ public int toIntValue(final Object value) {
+ return toIntValue(value, 0);
+ }
+
+ // ---------------------------------------------------------------- long
+
+ /**
+ * Converts value to Long.
+ */
+ public Long toLong(final Object value) {
+ return (Long) getConvert(Long.class).convert(value);
+ }
+
+ /**
+ * Converts value to Long. Returns default value
+ * when conversion result is null
+ */
+ public Long toLong(final Object value, final Long defaultValue) {
+ final Long result = toLong(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to long. Returns default value
+ * when conversion result is null.
+ */
+ public long toLongValue(final Object value, final long defaultValue) {
+ final Long result = toLong(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.longValue();
+ }
+
+ /**
+ * Converts value to long with common default value.
+ */
+ public long toLongValue(final Object value) {
+ return toLongValue(value, 0);
+ }
+
+ // ---------------------------------------------------------------- float
+
+ /**
+ * Converts value to Float.
+ */
+ public Float toFloat(final Object value) {
+ return (Float) getConvert(Float.class).convert(value);
+ }
+
+ /**
+ * Converts value to Float. Returns default value
+ * when conversion result is null
+ */
+ public Float toFloat(final Object value, final Float defaultValue) {
+ final Float result = toFloat(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to float. Returns default value
+ * when conversion result is null.
+ */
+ public float toFloatValue(final Object value, final float defaultValue) {
+ final Float result = toFloat(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.floatValue();
+ }
+
+ /**
+ * Converts value to float with common default value.
+ */
+ public float toFloatValue(final Object value) {
+ return toFloatValue(value, 0);
+ }
+
+ // ---------------------------------------------------------------- double
+
+ /**
+ * Converts value to Double.
+ */
+ public Double toDouble(final Object value) {
+ return (Double) getConvert(Double.class).convert(value);
+ }
+
+ /**
+ * Converts value to Double. Returns default value
+ * when conversion result is null
+ */
+ public Double toDouble(final Object value, final Double defaultValue) {
+ final Double result = toDouble(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to double. Returns default value
+ * when conversion result is null.
+ */
+ public double toDoubleValue(final Object value, final double defaultValue) {
+ final Double result = toDouble(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.doubleValue();
+ }
+
+ /**
+ * Converts value to double with common default value.
+ */
+ public double toDoubleValue(final Object value) {
+ return toDoubleValue(value, 0);
+ }
+
+ // ---------------------------------------------------------------- short
+
+ /**
+ * Converts value to Short.
+ */
+ public Short toShort(final Object value) {
+ return (Short) getConvert(Short.class).convert(value);
+ }
+
+ /**
+ * Converts value to Short. Returns default value
+ * when conversion result is null
+ */
+ public Short toShort(final Object value, final Short defaultValue) {
+ final Short result = toShort(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to short. Returns default value
+ * when conversion result is null.
+ */
+ public short toShortValue(final Object value, final short defaultValue) {
+ final Short result = toShort(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.shortValue();
+ }
+
+ /**
+ * Converts value to short with common default value.
+ */
+ public short toShortValue(final Object value) {
+ return toShortValue(value, (short) 0);
+ }
+
+ // ---------------------------------------------------------------- character
+
+ /**
+ * Converts value to Character.
+ */
+ public Character toCharacter(final Object value) {
+ return (Character) getConvert(Character.class).convert(value);
+ }
+
+ /**
+ * Converts value to Character. Returns default value
+ * when conversion result is null
+ */
+ public Character toCharacter(final Object value, final Character defaultValue) {
+ final Character result = toCharacter(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to char. Returns default value
+ * when conversion result is null.
+ */
+ public char toCharValue(final Object value, final char defaultValue) {
+ final Character result = toCharacter(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.charValue();
+ }
+
+ /**
+ * Converts value to char with common default value.
+ */
+ public char toCharValue(final Object value) {
+ return toCharValue(value, (char) 0);
+ }
+
+ // ---------------------------------------------------------------- byte
+
+ /**
+ * Converts value to Byte.
+ */
+ public Byte toByte(final Object value) {
+ return (Byte) getConvert(Byte.class).convert(value);
+ }
+
+ /**
+ * Converts value to Byte. Returns default value
+ * when conversion result is null
+ */
+ public Byte toByte(final Object value, final Byte defaultValue) {
+ final Byte result = toByte(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to byte. Returns default value
+ * when conversion result is null.
+ */
+ public byte toByteValue(final Object value, final byte defaultValue) {
+ final Byte result = toByte(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result.byteValue();
+ }
+
+ /**
+ * Converts value to byte with common default value.
+ */
+ public byte toByteValue(final Object value) {
+ return toByteValue(value, (byte) 0);
+ }
+
+ // ---------------------------------------------------------------- array
+
+
+
+ // ---------------------------------------------------------------- string
+
+ /**
+ * Converts value to String.
+ */
+ public String toString(final Object value) {
+ return (String) getConvert(String.class).convert(value);
+ }
+
+ /**
+ * Converts value to String. Returns default value
+ * when conversion result is null
+ */
+ public String toString(final Object value, final String defaultValue) {
+ final String result = toString(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+
+ // ---------------------------------------------------------------- bigs
+
+ /**
+ * Converts value to BigInteger.
+ */
+ public BigInteger toBigInteger(final Object value) {
+ return (BigInteger) getConvert(BigInteger.class).convert(value);
+ }
+
+ /**
+ * Converts value to BigInteger. Returns default value
+ * when conversion result is null
+ */
+ public BigInteger toBigInteger(final Object value, final BigInteger defaultValue) {
+ final BigInteger result = toBigInteger(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to BigDecimal.
+ */
+ public BigDecimal toBigDecimal(final Object value) {
+ return (BigDecimal) getConvert(BigDecimal.class).convert(value);
+ }
+
+ /**
+ * Converts value to BigDecimal. Returns default value
+ * when conversion result is null
+ */
+ public BigDecimal toBigDecimal(final Object value, final BigDecimal defaultValue) {
+ final BigDecimal result = toBigDecimal(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to LocalDate.
+ */
+ public LocalDate toLocalDate(final Object value) {
+ return (LocalDate) getConvert(LocalDate.class).convert(value);
+ }
+
+ /**
+ * Converts value to LocalDate. Returns default value
+ * when conversion result is null
+ */
+ public LocalDate toLocalDate(final Object value, final LocalDate defaultValue) {
+ final LocalDate result = toLocalDate(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+ /**
+ * Converts value to LocalDateTime.
+ */
+ public LocalDateTime toLocalDateTime(final Object value) {
+ return (LocalDateTime) getConvert(LocalDateTime.class).convert(value);
+ }
+
+ /**
+ * Converts value to LocalDateTime. Returns default value
+ * when conversion result is null
+ */
+ public LocalDateTime toLocalDateTime(final Object value, final LocalDateTime defaultValue) {
+ final LocalDateTime result = toLocalDateTime(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+
+ /**
+ * Converts value to LocalTime.
+ */
+ public LocalTime toLocalTime(final Object value) {
+ return (LocalTime) getConvert(LocalTime.class).convert(value);
+ }
+
+ /**
+ * Converts value to LocalTime. Returns default value
+ * when conversion result is null
+ */
+ public LocalTime toLocalTime(final Object value, final LocalTime defaultValue) {
+ final LocalTime result = toLocalTime(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * Converts value to Date.
+ */
+ public Date toDate(final Object value) {
+ return (Date) getConvert(Date.class).convert(value);
+ }
+
+ /**
+ * Converts value to LocalTime. Returns default value
+ * when conversion result is null
+ */
+ public Date toDate(final Object value, final Date defaultValue) {
+ final Date result = toDate(value);
+ if (result == null) {
+ return defaultValue;
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConversionException.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConversionException.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a97ff05d6098b4fdffee9185ae4ed564fb2d750
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConversionException.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert;
+
+
+import com.vonchange.common.util.exception.UncheckedException;
+
+/**
+ * Type conversion exception.
+ */
+public class TypeConversionException extends UncheckedException {
+
+ public TypeConversionException(final Throwable t) {
+ super(t);
+ }
+
+ public TypeConversionException(final String message) {
+ super(message);
+ }
+
+ public TypeConversionException(final String message, final Throwable t) {
+ super(message, t);
+ }
+
+ public TypeConversionException(final Object value) {
+ this("Conversion failed: " + value);
+ }
+
+ public TypeConversionException(final Object value, final Throwable t) {
+ this("Conversion failed: " + value, t);
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConvertCommon.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConvertCommon.java
new file mode 100644
index 0000000000000000000000000000000000000000..39fa19c525d5049e0418bec760ae8b156badc810
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConvertCommon.java
@@ -0,0 +1,19 @@
+package com.vonchange.common.util.bean.convert;
+
+public abstract class TypeConvertCommon implements TypeConverter{
+
+ public abstract T convert(Object value);
+
+ public T convert(final Object value, final T defaultValue){
+ if (value == null) {
+ return defaultValue;
+ }
+ try {
+ return convert(value);
+ }
+ catch (Exception e) {
+ return defaultValue;
+ }
+ }
+}
+
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d52fa1f1520502dd4120d2819a9a9f9b9183dfa
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/TypeConverter.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert;
+
+
+
+/**
+ * Object converter interface.
+ *
+ */
+public interface TypeConverter {
+
+ /**
+ * Converts object received as parameter into object of another class.
+ * For example, an Integer converter tries to convert given objects
+ * into target Integer object. Converters should try all reasonable
+ * ways of conversion into target object, depending on target type.
+ *
+ * @param value object to convert from
+ *
+ * @return resulting object converted to target type
+ * @throws TypeConversionException if conversion fails
+ */
+ T convert(Object value);
+
+ /**
+ * Converts object and returns default value if conversion fails.
+ */
+ T convert(final Object value, final T defaultValue);
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigDecimalConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigDecimalConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6812d94063ea1e6b9c1adb0b831c464d153b0f91
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigDecimalConverter.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+import java.math.BigDecimal;
+
+/**
+ * Converts given object to BigDecimal.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible
+ *
+ */
+public class BigDecimalConverter extends TypeConvertCommon implements TypeConverter {
+
+ @Override
+ public BigDecimal convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof BigDecimal) {
+ return (BigDecimal) value;
+ }
+ try {
+ return new BigDecimal(value.toString().trim());
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigIntegerConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigIntegerConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..434371ef7dfa607a284faa3a7b10149c2816313d
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BigIntegerConverter.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+import java.math.BigInteger;
+
+/**
+ * Converts given object to BigInteger.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible
+ *
+ */
+public class BigIntegerConverter extends TypeConvertCommon implements TypeConverter {
+
+ public BigInteger convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof BigInteger) {
+ return (BigInteger) value;
+ }
+ if (value instanceof Number) {
+ return new BigInteger(String.valueOf(((Number)value).longValue()));
+ }
+ try {
+ return new BigInteger(value.toString().trim());
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BooleanConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BooleanConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..3cc7b005468c70f6687db9c9c55a1ce464e62a30
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/BooleanConverter.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+import static com.vonchange.common.util.StringPool.FALSE;
+import static com.vonchange.common.util.StringPool.N;
+import static com.vonchange.common.util.StringPool.NO;
+import static com.vonchange.common.util.StringPool.OFF;
+import static com.vonchange.common.util.StringPool.ON;
+import static com.vonchange.common.util.StringPool.ONE;
+import static com.vonchange.common.util.StringPool.TRUE;
+import static com.vonchange.common.util.StringPool.Y;
+import static com.vonchange.common.util.StringPool.YES;
+import static com.vonchange.common.util.StringPool.ZERO;
+
+/**
+ * Converts given object to Boolean.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed. Then common boolean strings are matched:
+ * "yes", "y", "true", "on", "1" for true; and opposite values
+ * for false.
+ *
+ */
+public class BooleanConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Boolean convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Boolean.class) {
+ return (Boolean) value;
+ }
+
+ String stringValue = value.toString();
+ if (stringValue.isEmpty()) {
+ return Boolean.FALSE;
+ }
+
+ stringValue = stringValue.trim().toLowerCase();
+ if (stringValue.equals(YES) ||
+ stringValue.equals(Y) ||
+ stringValue.equals(TRUE) ||
+ stringValue.equals(ON) ||
+ stringValue.equals(ONE)) {
+ return Boolean.TRUE;
+ }
+ if (stringValue.equals(NO) ||
+ stringValue.equals(N) ||
+ stringValue.equals(FALSE) ||
+ stringValue.equals(OFF) ||
+ stringValue.equals(ZERO)) {
+ return Boolean.FALSE;
+ }
+
+ throw new TypeConversionException(value);
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteArrayConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteArrayConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..467c1dd639e03d948c7f9105e4105179de9bd1ac
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteArrayConverter.java
@@ -0,0 +1,198 @@
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.Converter;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+import java.sql.Blob;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class ByteArrayConverter extends TypeConvertCommon implements TypeConverter {
+ public static final char[] NUMBER_DELIMITERS = new char[] {',', ';', '\n'};
+
+
+ @Override
+ public byte[] convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ final Class valueClass = value.getClass();
+
+ if (!valueClass.isArray()) {
+ // source is not an array
+ return convertValueToArray(value);
+ }
+
+ // source is an array
+ return convertArrayToArray(value);
+ }
+
+ /**
+ * Converts type using type converter manager.
+ */
+ protected byte convertType(final Object value) {
+ return Converter.get().toByte(value);
+ //return typeConverterManager.convertType(value, byte.class).byteValue();
+ }
+
+ /**
+ * Creates an array with single element.
+ */
+ protected byte[] convertToSingleElementArray(final Object value) {
+ return new byte[] {convertType(value)};
+ }
+
+ /**
+ * Converts non-array value to array. Detects various
+ * types and collections, iterates them to make conversion
+ * and to create target array.
+ */
+ protected byte[] convertValueToArray(final Object value) {
+ if (value instanceof Blob) {
+ final Blob blob = (Blob) value;
+ try {
+ final long length = blob.length();
+ if (length > Integer.MAX_VALUE) {
+ throw new TypeConversionException("Blob is too big.");
+ }
+ return blob.getBytes(1, (int) length);
+ } catch (SQLException sex) {
+ throw new TypeConversionException(value, sex);
+ }
+ }
+
+ if (value instanceof Collection) {
+ final Collection collection = (Collection) value;
+ final byte[] target = new byte[collection.size()];
+
+ int i = 0;
+ for (final Object element : collection) {
+ target[i] = convertType(element);
+ i++;
+ }
+
+ return target;
+ }
+
+ if (value instanceof Iterable) {
+ final Iterable iterable = (Iterable) value;
+
+ final ArrayList byteArrayList = new ArrayList<>();
+
+ for (final Object element : iterable) {
+ final byte convertedValue = convertType(element);
+ byteArrayList.add(Byte.valueOf(convertedValue));
+ }
+
+ final byte[] array = new byte[byteArrayList.size()];
+
+ for (int i = 0; i < byteArrayList.size(); i++) {
+ final Byte b = byteArrayList.get(i);
+ array[i] = b.byteValue();
+ }
+
+ return array;
+ }
+
+ if (value instanceof CharSequence) {
+ final String[] strings = StringUtils.splitc(value.toString(), NUMBER_DELIMITERS);
+ return convertArrayToArray(strings);
+ }
+
+ // everything else:
+ return convertToSingleElementArray(value);
+ }
+
+ /**
+ * Converts array value to array.
+ */
+ protected byte[] convertArrayToArray(final Object value) {
+ final Class valueComponentType = value.getClass().getComponentType();
+
+ final byte[] result;
+
+ if (valueComponentType.isPrimitive()) {
+ result = convertPrimitiveArrayToArray(value, valueComponentType);
+ } else {
+ // convert object array to target array
+ final Object[] array = (Object[]) value;
+ result = new byte[array.length];
+
+ for (int i = 0; i < array.length; i++) {
+ result[i] = convertType(array[i]);
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Converts primitive array to target array.
+ */
+ protected byte[] convertPrimitiveArrayToArray(final Object value, final Class primitiveComponentType) {
+ byte[] result = null;
+
+ if (primitiveComponentType == byte.class) {
+ return (byte[]) value;
+ }
+
+ if (primitiveComponentType == int.class) {
+ final int[] array = (int[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == long.class) {
+ final long[] array = (long[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == float.class) {
+ final float[] array = (float[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == double.class) {
+ final double[] array = (double[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == short.class) {
+ final short[] array = (short[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == char.class) {
+ final char[] array = (char[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) array[i];
+ }
+ }
+ else if (primitiveComponentType == boolean.class) {
+ final boolean[] array = (boolean[]) value;
+ result = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ result[i] = (byte) (array[i] ? 1 : 0);
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..07d62487ab2003dbb57337aa1cac986ea64f0021
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ByteConverter.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to Byte.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class ByteConverter extends TypeConvertCommon implements TypeConverter {
+
+ @Override
+ public Byte convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value.getClass() == Byte.class) {
+ return (Byte) value;
+ }
+ if (value instanceof Number) {
+ return Byte.valueOf(((Number)value).byteValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Byte.valueOf((byte) 1) : Byte.valueOf((byte) 0);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Byte.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/CharacterConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/CharacterConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..76657093004ad5e82759295bf135bd3e7cda40fd
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/CharacterConverter.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to Character.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
Number is converted to char value
+ *
finally, toString() value of length 1 is converted to char
+ *
if string is longer, and made of digits, try to convert it to int first
+ *
+ */
+public class CharacterConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Character convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value.getClass() == Character.class) {
+ return (Character) value;
+ }
+ if (value instanceof Number) {
+ char c = (char) ((Number) value).intValue();
+ return Character.valueOf(c);
+ }
+ try {
+ String s = value.toString();
+ if (s.length() != 1) {
+ s = s.trim();
+ if (!StringUtils.containsOnlyDigitsAndSigns(s)) {
+ throw new TypeConversionException(value);
+ }
+
+ try {
+ char c = (char) Integer.parseInt(s);
+ return Character.valueOf(c);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+ return Character.valueOf(s.charAt(0));
+ } catch (IndexOutOfBoundsException ioobex) {
+ throw new TypeConversionException(value, ioobex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DateConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DateConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea026ae3647dad86ac9e5d915c51f4690873d47e
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DateConverter.java
@@ -0,0 +1,54 @@
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+import com.vonchange.common.util.time.TimeUtil;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateConverter extends TypeConvertCommon implements TypeConverter {
+
+ @Override
+ public Date convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof Date) {
+ return (Date) value;
+ }
+ if (value instanceof Calendar) {
+ return new Date(((Calendar)value).getTimeInMillis());
+ }
+ if (value instanceof LocalDateTime) {
+ return TimeUtil.toDate((LocalDateTime)value);
+ }
+ if (value instanceof LocalDate) {
+ return TimeUtil.toDate((LocalDate)value);
+ }
+ if (value instanceof Number) {
+ return new Date(((Number) value).longValue());
+ }
+
+ final String stringValue = value.toString().trim();
+
+ if (!StringUtils.containsOnlyDigits(stringValue)) {
+ // try to parse default string format
+ return TimeUtil.toDate(LocalDateTime.parse(stringValue));
+ }
+
+ try {
+ long milliseconds = Long.parseLong(stringValue);
+ return new Date(milliseconds);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DoubleConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DoubleConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..70df332461a431c3138c13ad055986d0f040ef06
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/DoubleConverter.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to Double.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class DoubleConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Double convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Double.class) {
+ return (Double) value;
+ }
+ if (value instanceof Number) {
+ return Double.valueOf(((Number)value).doubleValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Double.valueOf(1) : Double.valueOf(0);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Double.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/FloatConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/FloatConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e5bed8ca960d87816b43180d69aa23080bdd4d0
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/FloatConverter.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to Float.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class FloatConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Float convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Float.class) {
+ return (Float) value;
+ }
+ if (value instanceof Number) {
+ return Float.valueOf(((Number)value).floatValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Float.valueOf(1) : Float.valueOf(0);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Float.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/IntegerConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/IntegerConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c48ca0d9d4ef822ccb131a8e7c38dd2fd46387f
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/IntegerConverter.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to an Integer.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class IntegerConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Integer convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Integer.class) {
+ return (Integer) value;
+ }
+ if (value instanceof Number) {
+ return Integer.valueOf(((Number)value).intValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Integer.valueOf(1) : Integer.valueOf(0);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Integer.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9a881761b1cecac7c5809cbb28dda2806891743
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateConverter.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+import com.vonchange.common.util.time.TimeUtil;
+
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Calendar;
+import java.util.Date;
+
+public class LocalDateConverter extends TypeConvertCommon implements TypeConverter {
+ @Override
+ public LocalDate convert(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof LocalDateTime) {
+ return ((LocalDateTime)value).toLocalDate();
+ }
+ if (value instanceof Calendar) {
+ return TimeUtil.fromCalendar((Calendar) value).toLocalDate();
+ }
+ if (value instanceof Timestamp) {
+ return TimeUtil.fromMilliseconds(((Timestamp)value).getTime()).toLocalDate();
+ }
+ if (value instanceof Date) {
+ if(value instanceof java.sql.Date){
+ return ((java.sql.Date) value).toLocalDate();
+ }
+ return TimeUtil.fromDate((Date) value).toLocalDate();
+ }
+ if (value instanceof Number) {
+ return TimeUtil.fromMilliseconds(((Number)value).longValue()).toLocalDate();
+ }
+ if (value instanceof LocalTime) {
+ throw new TypeConversionException("Can't convert to date just from time: " + value);
+ }
+
+ String stringValue = value.toString().trim();
+
+ if (!StringUtils.containsOnlyDigits(stringValue)) {
+ // try to parse default string format
+ return LocalDate.parse(stringValue);
+ }
+
+ try {
+ return TimeUtil.fromMilliseconds(Long.parseLong(stringValue)).toLocalDate();
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateTimeConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateTimeConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8a3259b8c58ec1a29fc5a779494618af34e14cd
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalDateTimeConverter.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+import com.vonchange.common.util.time.TimeUtil;
+
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Calendar;
+import java.util.Date;
+
+public class LocalDateTimeConverter extends TypeConvertCommon implements TypeConverter {
+ @Override
+ public LocalDateTime convert(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof LocalDate) {
+ return LocalDateTime.of(((LocalDate) value), LocalTime.MIDNIGHT);
+ }
+ if (value instanceof Calendar) {
+ return TimeUtil.fromCalendar((Calendar) value);
+ }
+ if (value instanceof Timestamp) {
+ return TimeUtil.fromMilliseconds(((Timestamp)value).getTime());
+ }
+ if (value instanceof Date) {
+ return TimeUtil.fromDate((Date) value);
+ }
+ if (value instanceof Number) {
+ return TimeUtil.fromMilliseconds(((Number)value).longValue());
+ }
+ if (value instanceof LocalTime) {
+ throw new TypeConversionException("Can't convert to date just from time: " + value);
+ }
+
+ String stringValue = value.toString().trim();
+
+ if (!StringUtils.containsOnlyDigits(stringValue)) {
+ // try to parse default string format
+ return LocalDateTime.parse(stringValue);
+ }
+
+ try {
+ return TimeUtil.fromMilliseconds(Long.parseLong(stringValue));
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalTimeConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalTimeConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3ce6bfb5f1689629a2746a95952a028732dac6d
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LocalTimeConverter.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+import com.vonchange.common.util.time.TimeUtil;
+
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Calendar;
+import java.util.Date;
+
+public class LocalTimeConverter extends TypeConvertCommon implements TypeConverter {
+ @Override
+ public LocalTime convert(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof LocalDateTime) {
+ return ((LocalDateTime)value).toLocalTime();
+ }
+ if (value instanceof Calendar) {
+ return TimeUtil.fromCalendar((Calendar) value).toLocalTime();
+ }
+ if (value instanceof Timestamp) {
+ return TimeUtil.fromMilliseconds(((Timestamp)value).getTime()).toLocalTime();
+ }
+ if (value instanceof Date) {
+ return TimeUtil.fromDate((Date) value).toLocalTime();
+ }
+ if (value instanceof Number) {
+ return TimeUtil.fromMilliseconds(((Number)value).longValue()).toLocalTime();
+ }
+ if (value instanceof LocalDate) {
+ throw new TypeConversionException("Can't convert to time just from date: " + value);
+ }
+
+ String stringValue = value.toString().trim();
+
+ if (!StringUtils.containsOnlyDigits(stringValue)) {
+ // try to parse default string format
+ return LocalTime.parse(stringValue);
+ }
+
+ try {
+ return TimeUtil.fromMilliseconds(Long.parseLong(stringValue)).toLocalTime();
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LongConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LongConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7e62f6c9e85b9ca8cf4b03f2966bdf3847cbf11
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/LongConverter.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to a Long.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class LongConverter extends TypeConvertCommon implements TypeConverter {
+
+ public Long convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Long.class) {
+ return (Long) value;
+ }
+ if (value instanceof Number) {
+ return Long.valueOf(((Number)value).longValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Long.valueOf(1L) : Long.valueOf(0L);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Long.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ShortConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ShortConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..454f281aa8ed458f0b8ea63df13d0b03d242624f
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/ShortConverter.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+/**
+ * Converts given object to Short.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
object of destination type is simply casted
+ *
object is converted to string, trimmed, and then converted if possible.
+ *
+ * Number string may start with plus and minus sign.
+ */
+public class ShortConverter extends TypeConvertCommon implements TypeConverter {
+
+ @Override
+ public Short convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value.getClass() == Short.class) {
+ return (Short) value;
+ }
+ if (value instanceof Number) {
+ return Short.valueOf(((Number)value).shortValue());
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? Short.valueOf((short) 1) : Short.valueOf((short) 0);
+ }
+
+ try {
+ String stringValue = value.toString().trim();
+ if (StringUtils.startsWithChar(stringValue, '+')) {
+ stringValue = stringValue.substring(1);
+ }
+ return Short.valueOf(stringValue);
+ } catch (NumberFormatException nfex) {
+ throw new TypeConversionException(value, nfex);
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/StringConverter.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/StringConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..bce8ef888b9c3d4ccf4b13946577ffb9e7c80e3b
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/impl/StringConverter.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.bean.convert.impl;
+
+
+import com.vonchange.common.util.bean.convert.TypeConversionException;
+import com.vonchange.common.util.bean.convert.TypeConvertCommon;
+import com.vonchange.common.util.bean.convert.TypeConverter;
+
+import java.sql.Clob;
+import java.sql.SQLException;
+
+/**
+ * Converts given object to String.
+ * Conversion rules:
+ *
+ *
null value is returned as null
+ *
for CharSequence type returns toString value
+ *
Class returns cass name
+ *
char[] is used for creating string
+ *
arrays are converted to comma-separated list of toString values
+ *
Clob is converted
+ *
finally, toString() value is returned.
+ *
+ */
+public class StringConverter extends TypeConvertCommon implements TypeConverter {
+
+ @Override
+ public String convert(final Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ if (value instanceof CharSequence) { // for speed
+ return value.toString();
+ }
+ Class type = value.getClass();
+ if (type == Class.class) {
+ return ((Class) value).getName();
+ }
+
+ if (value instanceof Clob) {
+ Clob clob = (Clob) value;
+ try {
+ long length = clob.length();
+ if (length > Integer.MAX_VALUE) {
+ throw new TypeConversionException("Clob is too big.");
+ }
+ return clob.getSubString(1, (int) length);
+ } catch (SQLException sex) {
+ throw new TypeConversionException(value, sex);
+ }
+ }
+ return value.toString();
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/convert/package-info.java b/common-util/src/main/java/com/vonchange/common/util/bean/convert/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..1737d32b4cc7127d64b5e863dc57e082fcd63727
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/convert/package-info.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Slick and various type conversions. Following rules are applied:
+ *
+ *
There are no default values. If conversion fails, TypeConversionException will be thrown.
+ *
Conversion of null value is also null.
+ *
+ */
+package com.vonchange.common.util.bean.convert;
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/bean/package-info.java b/common-util/src/main/java/com/vonchange/common/util/bean/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b9c79306bff03f412c6ef8adede9da1672ced4d
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/bean/package-info.java
@@ -0,0 +1 @@
+package com.vonchange.common.util.bean;
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/exception/ExceptionUtil.java b/common-util/src/main/java/com/vonchange/common/util/exception/ExceptionUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..02b706ca1cd57ce42d60404064c5cfcf32c0ed25
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/exception/ExceptionUtil.java
@@ -0,0 +1,302 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.exception;
+
+
+import com.vonchange.common.util.StreamUtil;
+import com.vonchange.common.util.StringUtils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Few exception utilities.
+ */
+public class ExceptionUtil {
+
+ /**
+ * Returns current stack trace in form of array of stack trace elements.
+ * First stack trace element is removed.
+ * Since an exception is thrown internally, this method is slow.
+ */
+ @SuppressWarnings({"ThrowCaughtLocally"})
+ public static StackTraceElement[] getCurrentStackTrace() {
+ StackTraceElement[] ste = new Exception().getStackTrace();
+ if (ste.length > 1) {
+ StackTraceElement[] result = new StackTraceElement[ste.length - 1];
+ System.arraycopy(ste, 1, result, 0, ste.length - 1);
+ return result;
+ } else {
+ return ste;
+ }
+ }
+
+ // ---------------------------------------------------------------- exception stack trace
+
+ /**
+ * Returns stack trace filtered by class names.
+ */
+ public static StackTraceElement[] getStackTrace(final Throwable t, final String[] allow, final String[] deny) {
+ StackTraceElement[] st = t.getStackTrace();
+ ArrayList result = new ArrayList<>(st.length);
+
+ elementLoop:
+ for (StackTraceElement element : st) {
+ String className = element.getClassName();
+ if (allow != null) {
+ boolean validElemenet = false;
+ for (String filter : allow) {
+ if (className.contains(filter)) {
+ validElemenet = true;
+ break;
+ }
+ }
+ if (!validElemenet) {
+ continue;
+ }
+ }
+ if (deny != null) {
+ for (String filter : deny) {
+ if (className.contains(filter)) {
+ continue elementLoop;
+ }
+ }
+ }
+ result.add(element);
+ }
+ st = new StackTraceElement[result.size()];
+ return result.toArray(st);
+ }
+
+ /**
+ * Returns stack trace chain filtered by class names.
+ */
+ public static StackTraceElement[][] getStackTraceChain(Throwable t, final String[] allow, final String[] deny) {
+ ArrayList result = new ArrayList<>();
+ while (t != null) {
+ StackTraceElement[] stack = getStackTrace(t, allow, deny);
+ result.add(stack);
+ t = t.getCause();
+ }
+ StackTraceElement[][] allStacks = new StackTraceElement[result.size()][];
+ for (int i = 0; i < allStacks.length; i++) {
+ allStacks[i] = result.get(i);
+ }
+ return allStacks;
+ }
+
+
+ /**
+ * Returns exception chain starting from top up to root cause.
+ */
+ public static Throwable[] getExceptionChain(Throwable throwable) {
+ ArrayList list = new ArrayList<>();
+ list.add(throwable);
+ while ((throwable = throwable.getCause()) != null) {
+ list.add(throwable);
+ }
+ Throwable[] result = new Throwable[list.size()];
+ return list.toArray(result);
+ }
+
+
+ // ---------------------------------------------------------------- exception to string
+
+
+ /**
+ * Prints stack trace into a String.
+ */
+ public static String exceptionStackTraceToString(final Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+
+ t.printStackTrace(pw);
+
+ StreamUtil.close(pw);
+ StreamUtil.close(sw);
+
+ return sw.toString();
+ }
+
+ /**
+ * Prints full exception stack trace, from top to root cause, into a String.
+ */
+ public static String exceptionChainToString(Throwable t) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ while (t != null) {
+ t.printStackTrace(pw);
+ t = t.getCause();
+ }
+
+ StreamUtil.close(pw);
+ StreamUtil.close(sw);
+
+ return sw.toString();
+ }
+
+ /**
+ * Build a message for the given base message and its cause.
+ */
+ public static String buildMessage(final String message, Throwable cause) {
+ if (cause != null) {
+ cause = getRootCause(cause);
+ StringBuilder buf = new StringBuilder();
+ if (message != null) {
+ buf.append(message).append("; ");
+ }
+ buf.append("<--- ").append(cause);
+ return buf.toString();
+ } else {
+ return message;
+ }
+ }
+
+ // ---------------------------------------------------------------- root cause
+
+ /**
+ * Introspects the Throwable to obtain the root cause.
+ *
+ * This method walks through the exception chain to the last element,
+ * "root" of the tree, and returns that exception. If no root cause found
+ * returns provided throwable.
+ */
+ public static Throwable getRootCause(final Throwable throwable) {
+ Throwable cause = throwable.getCause();
+ if (cause == null) {
+ return throwable;
+ }
+
+ Throwable t = throwable;
+
+ // defend against (malicious?) circularity
+ for (int i = 0; i < 1000; i++) {
+ cause = t.getCause();
+ if (cause == null) {
+ return t;
+ }
+ t = cause;
+ }
+
+ return throwable;
+ }
+
+ /**
+ * Finds throwing cause in exception stack. Returns throwable object if cause class is matched.
+ * Otherwise, returns null.
+ */
+ @SuppressWarnings({"unchecked"})
+ public static T findCause(Throwable throwable, final Class cause) {
+ while (throwable != null) {
+ if (throwable.getClass().equals(cause)) {
+ return (T) throwable;
+ }
+ throwable = throwable.getCause();
+ }
+ return null;
+ }
+
+
+ // ---------------------------------------------------------------- sql
+
+ /**
+ * Rolls up SQL exceptions by taking each proceeding exception
+ * and making it a child of the previous using the setNextException
+ * method of SQLException.
+ */
+ public static SQLException rollupSqlExceptions(final Collection exceptions) {
+ SQLException parent = null;
+ for (SQLException exception : exceptions) {
+ if (parent != null) {
+ exception.setNextException(parent);
+ }
+ parent = exception;
+ }
+ return parent;
+ }
+
+ // ---------------------------------------------------------------- misc
+
+ /**
+ * Throws checked exceptions in un-checked manner.
+ */
+ public static void throwRuntimeException(final Throwable throwable) {
+ throw wrapToRuntimeException(throwable);
+ }
+
+ /**
+ * Returns non-null message for a throwable.
+ */
+ public static String message(final Throwable throwable) {
+ String message = throwable.getMessage();
+
+ if (StringUtils.isBlank(message)) {
+ message = throwable.toString();
+ }
+
+ return message;
+ }
+
+ /**
+ * Wraps exception to {@code RuntimeException}.
+ */
+ public static RuntimeException wrapToRuntimeException(final Throwable throwable) {
+ if (throwable instanceof RuntimeException) {
+ return (RuntimeException) throwable;
+ }
+ return new RuntimeException(throwable);
+ }
+ public static Exception wrapToException(final Throwable throwable) {
+ if (throwable instanceof Exception) {
+ return (Exception) throwable;
+ }
+ return new RuntimeException(throwable);
+ }
+
+ /**
+ * Unwraps invocation and undeclared exceptions to real cause.
+ */
+ public static Throwable unwrapThrowable(final Throwable wrappedThrowable) {
+ Throwable unwrapped = wrappedThrowable;
+ while (true) {
+ if (unwrapped instanceof InvocationTargetException) {
+ unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
+ }
+ else if (unwrapped instanceof UndeclaredThrowableException) {
+ unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
+ }
+ else {
+ return unwrapped;
+ }
+ }
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/exception/UncheckedException.java b/common-util/src/main/java/com/vonchange/common/util/exception/UncheckedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bf848938eb3031f72d8b46f90143880424d28c2
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/exception/UncheckedException.java
@@ -0,0 +1,214 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.exception;
+
+
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.util.concurrent.Callable;
+
+/**
+ * Unchecked exception and also a wrapper for checked exceptions.
+ */
+@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"})
+public class UncheckedException extends RuntimeException {
+
+ protected final Throwable cause;
+
+ /**
+ * Divider between causes printouts.
+ */
+ protected static final String CAUSE_DIV = "---[cause]------------------------------------------------------------------------";
+
+ /**
+ * If set to true stack trace will be enhanced with cause's stack traces.
+ */
+ protected final boolean showCauseDetails;
+
+ // ---------------------------------------------------------------- constructors
+
+ public UncheckedException(final Throwable t) {
+ super(t.getMessage());
+ cause = t;
+ this.showCauseDetails = true;
+ }
+
+ public UncheckedException(final Throwable t, final boolean showCauseDetails) {
+ super(t.getMessage());
+ cause = t;
+ this.showCauseDetails = showCauseDetails;
+ }
+
+ public UncheckedException() {
+ super();
+ cause = null;
+ this.showCauseDetails = false;
+ }
+
+ public UncheckedException(final String message) {
+ super(message);
+ cause = null;
+ this.showCauseDetails = false;
+ }
+
+ public UncheckedException(final String message, final Throwable t) {
+ super(message, t);
+ cause = t;
+ this.showCauseDetails = true;
+ }
+
+ public UncheckedException(final String message, final Throwable t, final boolean showCauseDetails) {
+ super(message, t);
+ cause = t;
+ this.showCauseDetails = showCauseDetails;
+ }
+
+ // ---------------------------------------------------------------- stack trace
+
+ @Override
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ @Override
+ public void printStackTrace(final PrintStream ps) {
+ synchronized (ps) {
+ super.printStackTrace(ps);
+ if ((cause != null) && showCauseDetails) {
+ Throwable rootCause = ExceptionUtil.getRootCause(cause);
+ ps.println(CAUSE_DIV);
+ rootCause.printStackTrace(ps);
+ }
+ }
+ }
+
+ @Override
+ public void printStackTrace(final PrintWriter pw) {
+ synchronized (pw) {
+ super.printStackTrace(pw);
+ if ((cause != null) && showCauseDetails) {
+ Throwable rootCause = ExceptionUtil.getRootCause(cause);
+ pw.println(CAUSE_DIV);
+ rootCause.printStackTrace(pw);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------- txt
+
+ /**
+ * Returns the detail message, including the message from the nested exception if there is one.
+ */
+ @Override
+ public String getMessage() {
+ return ExceptionUtil.buildMessage(super.getMessage(), cause);
+ }
+
+ // ---------------------------------------------------------------- wrap
+
+ /**
+ * Wraps checked exceptions in a UncheckedException.
+ * Unchecked exceptions are not wrapped.
+ */
+ public static V callAndWrapException(final Callable callable) {
+ try {
+ return callable.call();
+ }
+ catch (IOException ioex) {
+ throw new UncheckedIOException(ioex);
+ }
+ catch (RuntimeException rtex) {
+ throw rtex;
+ }
+ catch (Exception t) {
+ throw new UncheckedException(t);
+ }
+ }
+
+ @FunctionalInterface
+ public interface CallableVoid {
+ public void call() throws Exception;
+ }
+
+ /**
+ * Wraps checked exceptions in a UncheckedException.
+ * Unchecked exceptions are not wrapped.
+ */
+ public static void runAndWrapException(final CallableVoid callable) {
+ try {
+ callable.call();
+ }
+ catch (IOException ioex) {
+ throw new UncheckedIOException(ioex);
+ }
+ catch (RuntimeException rtex) {
+ throw rtex;
+ }
+ catch (Exception t) {
+ throw new UncheckedException(t);
+ }
+ }
+
+ /**
+ * Wraps all exceptions in a UncheckedException
+ */
+ public static RuntimeException wrap(final Throwable t) {
+ return new UncheckedException(t);
+ }
+
+ /**
+ * Wraps all exceptions in a UncheckedException
+ */
+ public static RuntimeException wrap(final Throwable t, final String message) {
+ return new UncheckedException(message, t);
+ }
+
+
+ // ---------------------------------------------------------------- cause
+
+ /**
+ * Re-throws cause if exists.
+ */
+ public void rethrow() throws Throwable {
+ if (cause == null) {
+ return;
+ }
+ throw cause;
+ }
+
+ /**
+ * Returns exception cause.
+ */
+ @Override
+ public Throwable getCause() {
+ return cause;
+ }
+
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/io/CharArraySequence.java b/common-util/src/main/java/com/vonchange/common/util/io/CharArraySequence.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fce2989066dbf2901e437b436b6d696a860b9da
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/io/CharArraySequence.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.io;
+
+/**
+ * Simple {@code CharSequence} wrapper of the char array.
+ * For {@code Appendable} version use
+ */
+public class CharArraySequence implements CharSequence {
+
+ public static final CharSequence EMPTY = CharArraySequence.of();
+
+ private final char[] buffer;
+ private final int off, len;
+
+ /**
+ * Static constructor that creates a char sequence using provided char array.
+ */
+ public static CharArraySequence of(final char... value) {
+ return new CharArraySequence(value);
+ }
+
+ public static CharArraySequence of(final char[] value, final int offset, final int len) {
+ return new CharArraySequence(value, offset, len);
+ }
+
+ /**
+ * Static constructor that creates a char sequence by making a copy of provided char array.
+ */
+ public static CharArraySequence from(final char[] value, final int offset, final int len) {
+ final char[] buffer = new char[value.length];
+
+ System.arraycopy(value, offset, buffer, 0, len);
+
+ return new CharArraySequence(buffer);
+ }
+
+ public CharArraySequence(final char[] value) {
+ buffer = value;
+ off = 0;
+ len = value.length;
+ }
+
+ public CharArraySequence(final char[] value, final int offset, final int length) {
+ if ((offset | length | offset + length | value.length - offset - length) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ buffer = value;
+ off = offset;
+ len = length;
+ }
+
+ /**
+ * Ctor without the bounds check.
+ */
+ private CharArraySequence(final int offset, final int length, final char[] value) {
+ off = offset;
+ len = length;
+ buffer = value;
+ }
+
+ @Override
+ public int length() { return len; }
+
+ @Override
+ public String toString() { return String.valueOf(buffer, off, len); }
+
+ @Override
+ public char charAt(final int index) {
+ //if ((index | len - index - 1) < 0) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ return buffer[off + index];
+ }
+
+ @Override
+ public CharSequence subSequence(final int start, final int end) {
+ final int count = end - start;
+ final int rem = len - end;
+
+ if ((start | end | count | rem) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if ((start | rem) == 0) {
+ return this;
+ }
+
+ return new CharArraySequence(off + start, count, buffer);
+ }
+
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/io/FastCharArrayWriter.java b/common-util/src/main/java/com/vonchange/common/util/io/FastCharArrayWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..3083bfed47c110e85fb259cf2de6d0868fb69755
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/io/FastCharArrayWriter.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.io;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Similar to but for {@link Writer}.
+ */
+public class FastCharArrayWriter extends Writer {
+
+ private final FastCharBuffer buffer;
+
+ /**
+ * Creates a new writer. The buffer capacity is
+ * initially 1024 bytes, though its size increases if necessary.
+ */
+ public FastCharArrayWriter() {
+ this(1024);
+ }
+
+ /**
+ * Creates a new char array {@link Writer}, with a buffer capacity of
+ * the specified size, in bytes.
+ *
+ * @param size the initial size.
+ * @throws IllegalArgumentException if size is negative.
+ */
+ public FastCharArrayWriter(final int size) {
+ buffer = new FastCharBuffer(size);
+ }
+
+ /**
+ * @see Writer#write(char[], int, int)
+ */
+ @Override
+ public void write(final char[] b, final int off, final int len) {
+ buffer.append(b, off, len);
+ }
+
+ /**
+ * Writes single byte.
+ */
+ @Override
+ public void write(final int b) {
+ buffer.append((char) b);
+ }
+
+ @Override
+ public void write(final String s, final int off, final int len) {
+ write(s.toCharArray(), off, len);
+ }
+
+ /**
+ * @see CharArrayWriter#size()
+ */
+ public int size() {
+ return buffer.size();
+ }
+
+ /**
+ * Closing a {@link FastCharArrayWriter} has no effect. The methods in
+ * this class can be called after the stream has been closed without
+ * generating an {@link IOException}.
+ */
+ @Override
+ public void close() {
+ //nop
+ }
+
+ /**
+ * Flushing a {@link FastCharArrayWriter} has no effects.
+ */
+ @Override
+ public void flush() {
+ //nop
+ }
+
+ /**
+ * @see CharArrayWriter#reset()
+ */
+ public void reset() {
+ buffer.clear();
+ }
+
+ /**
+ * @see CharArrayWriter#writeTo(Writer)
+ */
+ public void writeTo(final Writer out) throws IOException {
+ out.write(buffer.toArray());
+ }
+
+ /**
+ * @see CharArrayWriter#toCharArray()
+ */
+ public char[] toCharArray() {
+ return buffer.toArray();
+ }
+
+ /**
+ * @see CharArrayWriter#toString()
+ */
+ @Override
+ public String toString() {
+ return new String(toCharArray());
+ }
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/io/FastCharBuffer.java b/common-util/src/main/java/com/vonchange/common/util/io/FastCharBuffer.java
new file mode 100644
index 0000000000000000000000000000000000000000..28b7ef040e92b09636a30a0d44baa29eb621035f
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/io/FastCharBuffer.java
@@ -0,0 +1,209 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.io;
+
+import java.util.Arrays;
+
+/**
+ * Faster {@code char} buffer.
+ */
+public class FastCharBuffer implements CharSequence, Appendable {
+
+ private char[] buffer;
+ private int offset;
+
+ /**
+ * Creates a new {@code char} buffer. The buffer capacity is
+ * initially 64 chars, though its size increases if necessary.
+ */
+ public FastCharBuffer() {
+ this.buffer = new char[64];
+ }
+
+ /**
+ * Creates a new {@code char} buffer, with a buffer capacity of
+ * the specified size, in chars.
+ *
+ * @param size the initial size.
+ * @throws IllegalArgumentException if size is negative.
+ */
+ public FastCharBuffer(final int size) {
+ this.buffer = new char[size];
+ }
+
+ /**
+ * Grows the buffer.
+ */
+ private void grow(final int minCapacity) {
+ final int oldCapacity = buffer.length;
+ int newCapacity = oldCapacity << 1;
+ if (newCapacity - minCapacity < 0) {
+ // special case, min capacity is larger then a grow
+ newCapacity = minCapacity + 512;
+ }
+ buffer = Arrays.copyOf(buffer, newCapacity);
+ }
+
+ /**
+ * Appends single {@code char} to buffer.
+ */
+ @Override
+ public FastCharBuffer append(final char element) {
+ if (offset - buffer.length >= 0) {
+ grow(offset);
+ }
+
+ buffer[offset++] = element;
+ return this;
+ }
+
+ /**
+ * Appends {@code char} array to buffer.
+ */
+ public FastCharBuffer append(final char[] array, final int off, final int len) {
+ if (offset + len - buffer.length > 0) {
+ grow(offset + len);
+ }
+
+ System.arraycopy(array, off, buffer, offset, len);
+ offset += len;
+ return this;
+ }
+
+ /**
+ * Appends {@code char} array to buffer.
+ */
+ public FastCharBuffer append(final char[] array) {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * Appends another fast buffer to this one.
+ */
+ public FastCharBuffer append(final FastCharBuffer buff) {
+ if (buff.offset == 0) {
+ return this;
+ }
+ append(buff.buffer, 0, buff.offset);
+ return this;
+ }
+
+ /**
+ * Returns buffer size.
+ */
+ public int size() {
+ return offset;
+ }
+
+ @Override
+ public int length() {
+ return offset;
+ }
+
+ /**
+ * Tests if this buffer has no elements.
+ */
+ public boolean isEmpty() {
+ return offset == 0;
+ }
+
+ /**
+ * Resets the buffer content.
+ */
+ public void clear() {
+ offset = 0;
+ }
+
+ /**
+ * Creates {@code char} array from buffered content.
+ */
+ public char[] toArray() {
+ return Arrays.copyOf(buffer, offset);
+ }
+
+ /**
+ * Creates {@code char} subarray from buffered content.
+ */
+ public char[] toArray(final int start, final int len) {
+ final char[] array = new char[len];
+
+ if (len == 0) {
+ return array;
+ }
+
+ System.arraycopy(buffer, start, array, 0, len);
+
+ return array;
+ }
+
+ /**
+ * Returns {@code char} element at given index.
+ */
+ public char get(final int index) {
+ if (index >= offset) {
+ throw new IndexOutOfBoundsException();
+ }
+ return buffer[index];
+ }
+
+ /**
+ * Appends character sequence to buffer.
+ */
+ @Override
+ public FastCharBuffer append(final CharSequence csq) {
+ append(csq, 0, csq.length());
+ return this;
+ }
+
+ /**
+ * Appends character sequence to buffer.
+ */
+ @Override
+ public FastCharBuffer append(final CharSequence csq, final int start, final int end) {
+ for (int i = start; i < end; i++) {
+ append(csq.charAt(i));
+ }
+ return this;
+ }
+ @Override
+ public char charAt(final int index) {
+ return get(index);
+ }
+
+ @Override
+ public CharSequence subSequence(final int start, final int end) {
+ return new CharArraySequence(buffer, start, end - start);
+ }
+
+ /**
+ * Returns a String of the char buffer.
+ * Please use {@code StringBuilder} instead, it is faster.
+ */
+ @Override
+ public String toString() {
+ return new String(toArray());
+ }
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/io/UnicodeInputStream.java b/common-util/src/main/java/com/vonchange/common/util/io/UnicodeInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1cfecd7cb22ee9abc397d9d272499839628a4d2
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/io/UnicodeInputStream.java
@@ -0,0 +1,200 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+/**
+ * Unicode input stream for detecting UTF encodings and reading BOM characters.
+ * Detects following BOMs:
+ *
+ *
UTF-8
+ *
UTF-16BE
+ *
UTF-16LE
+ *
UTF-32BE
+ *
UTF-32LE
+ *
+ */
+public class UnicodeInputStream extends InputStream {
+
+ public static final int MAX_BOM_SIZE = 4;
+
+ private final PushbackInputStream internalInputStream;
+ private boolean initialized;
+ private int BOMSize = -1;
+ private String encoding;
+ private final String targetEncoding;
+
+ /**
+ * Creates new unicode stream. It works in two modes: detect mode and read mode.
+ *
+ * Detect mode is active when target encoding is not specified.
+ * In detect mode, it tries to detect encoding from BOM if exist.
+ * If BOM doesn't exist, encoding is not detected.
+ *
+ * Read mode is active when target encoding is set. Then this stream reads
+ * optional BOM for given encoding. If BOM doesn't exist, nothing is skipped.
+ */
+ public UnicodeInputStream(final InputStream in, final String targetEncoding) {
+ internalInputStream = new PushbackInputStream(in, MAX_BOM_SIZE);
+ this.targetEncoding = targetEncoding;
+ }
+
+ /**
+ * Returns detected UTF encoding or {@code null} if no UTF encoding has been detected (i.e. no BOM).
+ * If stream is not read yet, it will be {@link #init() initalized} first.
+ */
+ public String getDetectedEncoding() {
+ if (!initialized) {
+ try {
+ init();
+ } catch (IOException ioex) {
+ throw new IllegalStateException(ioex);
+ }
+ }
+ return encoding;
+ }
+
+ public static final byte[] BOM_UTF32_BE = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0xFE, (byte) 0xFF};
+ public static final byte[] BOM_UTF32_LE = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0x00, (byte) 0x00};
+ public static final byte[] BOM_UTF8 = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
+ public static final byte[] BOM_UTF16_BE = new byte[]{(byte) 0xFE, (byte) 0xFF};
+ public static final byte[] BOM_UTF16_LE = new byte[]{(byte) 0xFF, (byte) 0xFE};
+
+ /**
+ * Detects and decodes encoding from BOM character.
+ * Reads ahead four bytes and check for BOM marks.
+ * Extra bytes are unread back to the stream, so only
+ * BOM bytes are skipped.
+ */
+ protected void init() throws IOException {
+ if (initialized) {
+ return;
+ }
+
+ if (targetEncoding == null) {
+
+ // DETECT MODE
+
+ byte[] bom = new byte[MAX_BOM_SIZE];
+ int n = internalInputStream.read(bom, 0, bom.length);
+ int unread;
+
+ if ((bom[0] == BOM_UTF32_BE[0]) && (bom[1] == BOM_UTF32_BE[1]) && (bom[2] == BOM_UTF32_BE[2]) && (bom[3] == BOM_UTF32_BE[3])) {
+ encoding = "UTF-32BE";
+ unread = n - 4;
+ } else if ((bom[0] == BOM_UTF32_LE[0]) && (bom[1] == BOM_UTF32_LE[1]) && (bom[2] == BOM_UTF32_LE[2]) && (bom[3] == BOM_UTF32_LE[3])) {
+ encoding = "UTF-32LE";
+ unread = n - 4;
+ } else if ((bom[0] == BOM_UTF8[0]) && (bom[1] == BOM_UTF8[1]) && (bom[2] == BOM_UTF8[2])) {
+ encoding = "UTF-8";
+ unread = n - 3;
+ } else if ((bom[0] == BOM_UTF16_BE[0]) && (bom[1] == BOM_UTF16_BE[1])) {
+ encoding = "UTF-16BE";
+ unread = n - 2;
+ } else if ((bom[0] == BOM_UTF16_LE[0]) && (bom[1] == BOM_UTF16_LE[1])) {
+ encoding = "UTF-16LE";
+ unread = n - 2;
+ } else {
+ // BOM not found, unread all bytes
+ unread = n;
+ }
+
+ BOMSize = MAX_BOM_SIZE - unread;
+
+ if (unread > 0) {
+ internalInputStream.unread(bom, (n - unread), unread);
+ }
+ } else {
+
+ // READ MODE
+
+ byte[] bom = null;
+
+ if (targetEncoding.equals("UTF-8")) {
+ bom = BOM_UTF8;
+ } else if (targetEncoding.equals("UTF-16LE")) {
+ bom = BOM_UTF16_LE;
+ } else if (targetEncoding.equals("UTF-16BE") || targetEncoding.equals("UTF-16")) {
+ bom = BOM_UTF16_BE;
+ } else if (targetEncoding.equals("UTF-32LE")) {
+ bom = BOM_UTF32_LE;
+ } else if (targetEncoding.equals("UTF-32BE") || targetEncoding.equals("UTF-32")) {
+ bom = BOM_UTF32_BE;
+ } else {
+ // no UTF encoding, no BOM
+ }
+
+ if (bom != null) {
+ byte[] fileBom = new byte[bom.length];
+ int n = internalInputStream.read(fileBom, 0, bom.length);
+
+ boolean bomDetected = true;
+ for (int i = 0; i < n; i++) {
+ if (fileBom[i] != bom[i]) {
+ bomDetected = false;
+ break;
+ }
+ }
+
+ if (!bomDetected) {
+ internalInputStream.unread(fileBom, 0, fileBom.length);
+ }
+ }
+ }
+
+ initialized = true;
+ }
+
+ /**
+ * Closes input stream. If stream was not used, encoding
+ * will be unavailable.
+ */
+ @Override
+ public void close() throws IOException {
+ internalInputStream.close();
+ }
+
+ /**
+ * Reads byte from the stream.
+ */
+ @Override
+ public int read() throws IOException {
+ init();
+ return internalInputStream.read();
+ }
+
+ /**
+ * Returns BOM size in bytes.
+ * Returns -1 if BOM not found.
+ */
+ public int getBOMSize() {
+ return BOMSize;
+ }
+
+}
\ No newline at end of file
diff --git a/common-util/src/main/java/com/vonchange/common/util/map/HashMap.java b/common-util/src/main/java/com/vonchange/common/util/map/HashMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3f8b0cf25fed3b6dc53d6d8c446b27608fa6e38
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/map/HashMap.java
@@ -0,0 +1,27 @@
+package com.vonchange.common.util.map;
+
+import java.util.Map;
+
+/**
+ *支持链式调用的HashMap
+ * @author von_change@163.com
+ * 2015-6-14 下午10:37:59
+ * @param
+ * @param
+ */
+public class HashMap extends java.util.HashMap implements Map {
+
+
+ public HashMap() {
+ super();
+ }
+
+ public HashMap(Map extends K, ? extends V> map) {
+ super(map);
+ }
+
+ public HashMap set(K key, V value) {
+ super.put(key, value);
+ return this;
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/map/MyHashMap.java b/common-util/src/main/java/com/vonchange/common/util/map/MyHashMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..27c1a5b74e81d44b81e1474cd5c80860ad96d5b2
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/map/MyHashMap.java
@@ -0,0 +1,36 @@
+package com.vonchange.common.util.map;
+
+import java.util.Map;
+
+
+/**
+ *支持链式调用的HashMap
+ * @author vonchange@163.com
+ */
+public class MyHashMap extends HashMap implements Map{
+
+
+ public MyHashMap() {
+ }
+
+ public MyHashMap(Map map) {
+ if (null == map) {
+ map = new MyHashMap();
+ }
+ for (Entry entry : map.entrySet()) {
+ this.set(entry.getKey(), entry.getValue());
+ }
+ }
+ @Override
+ public MyHashMap set(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+ public MyHashMap setAll(Map map) {
+ super.putAll(map);
+ return this;
+ }
+ public MyHashMap put(String key, Object value) {
+ return this.set(key, value);
+ }
+}
diff --git a/common-util/src/main/java/com/vonchange/common/util/time/TimeUtil.java b/common-util/src/main/java/com/vonchange/common/util/time/TimeUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ded0983229325218eadb335e6162094b659f93d
--- /dev/null
+++ b/common-util/src/main/java/com/vonchange/common/util/time/TimeUtil.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package com.vonchange.common.util.time;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class TimeUtil {
+
+ public static final int SECONDS_IN_DAY = 60 * 60 * 24;
+ public static final long MILLIS_IN_DAY = 1000L * SECONDS_IN_DAY;
+
+ public static final SimpleDateFormat HTTP_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
+
+ /**
+ * Converts local date to Date.
+ */
+ public static Date toDate(final LocalDate localDate) {
+ return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+ }
+ /**
+ * Converts local date time to Date.
+ */
+ public static Date toDate(final LocalDateTime localDateTime) {
+ return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+ }
+
+ /**
+ * Converts local date time to Calendar.
+ */
+ public static Calendar toCalendar(final LocalDateTime localDateTime) {
+ return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
+ }
+
+ /**
+ * Converts local date time to Calendar and setting time to midnight.
+ */
+ public static Calendar toCalendar(final LocalDate localDate) {
+ return GregorianCalendar.from(ZonedDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneId.systemDefault()));
+ }
+
+ /**
+ * Converts local date time to epoh milliseconds.
+ */
+ public static long toMilliseconds(final LocalDateTime localDateTime) {
+ return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ /**
+ * Converts local date time to epoh milliseconds assuming start of the day as time point.
+ */
+ public static long toMilliseconds(final LocalDate localDate) {
+ return toMilliseconds(localDate.atStartOfDay());
+ }
+
+
+ public static LocalDateTime fromCalendar(final Calendar calendar) {
+ final TimeZone tz = calendar.getTimeZone();
+ final ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
+ return LocalDateTime.ofInstant(calendar.toInstant(), zid);
+ }
+
+ public static LocalDateTime fromDate(final Date date) {
+ return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+ }
+
+ public static LocalDateTime fromMilliseconds(final long milliseconds) {
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
+ }
+
+
+ /**
+ * Formats time to HTTP date/time format. Note that number of milliseconds
+ * is lost.
+ */
+ public static String formatHttpDate(final long millis) {
+ final Date date = new Date(millis);
+ return HTTP_DATE_FORMAT.format(date);
+ }
+
+ /**
+ * Parses the HTTP date/time format. Returns -1 if given string
+ * is invalid.
+ */
+ public static long parseHttpTime(final String time) {
+ if (time == null) {
+ return -1;
+ }
+
+ try {
+ return TimeUtil.HTTP_DATE_FORMAT.parse(time).getTime();
+ }
+ catch (ParseException e) {
+ return -1;
+ }
+ }
+
+}
diff --git a/mybatis-mini/pom.xml b/mybatis-mini/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..98478183f31e5931dce2a500b9cc58a264c01310
--- /dev/null
+++ b/mybatis-mini/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ mybatis-mini-parant
+ com.vonchange.common
+ 2.3.9
+
+ 4.0.0
+ mybatis-mini
+ 2.3.9
+
+
+ com.vonchange.common
+ mybatis-template
+ 2.3.9
+
+
+ com.vonchange.common
+ jsqlparser
+ 1.1
+
+
+
+ org.springframework.data
+ spring-data-commons
+ 2.1.5.RELEASE
+ provided
+
+
+ org.springframework
+ spring-jdbc
+ 5.1.5.RELEASE
+ provided
+
+
+
+
+
\ No newline at end of file
diff --git a/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/ConstantJdbc.java b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/ConstantJdbc.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d0ceecade7dfc560bc499b59678da54412a6f83
--- /dev/null
+++ b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/ConstantJdbc.java
@@ -0,0 +1,24 @@
+package com.vonchange.jdbc.abstractjdbc.config;
+
+/**
+ * Created by 冯昌义 on 2018/3/20.
+ */
+public class ConstantJdbc {
+ private ConstantJdbc() { throw new IllegalStateException("Utility class");}
+ public static class PageParam{
+ public static final String COUNT = "_count";
+ public static final String AT = "@";
+ }
+ public static final String ISSQLFLAG= "@sql";
+ public static final String ISMDFLAG= "@md";
+ public static final String MAINFLAG= "main";
+ public static final String MDID= "### id";
+ public static final String MDVERSION= "#### version";
+ public static final String MDINITVERSION= "1.0";
+ public static class DataSource{
+ public static final String DEFAULT = "dataSource";
+ public static final String FLAG = "@ds:";
+ }
+ public static final String COUNTFLAG = "Count";
+ public static final String MAPFIELDSPLIT = "#";
+}
diff --git a/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/Constants.java b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..257e62cf900b492571312f9523a72034df91839d
--- /dev/null
+++ b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/config/Constants.java
@@ -0,0 +1,53 @@
+package com.vonchange.jdbc.abstractjdbc.config;
+
+
+/**
+ * 数据字典
+ * @author song.chen
+ *
+ */
+public class Constants {
+ private Constants() { throw new IllegalStateException("Utility class");}
+ /**
+ * mardown 配置里的信息
+ */
+ public static class Markdown{
+ public static final String TABLES = "tables";
+ public static final String IDPREF = "--";
+ }
+ public enum EnumRWType {
+ read(0,"读"),write(0,"写");
+ private Integer value;
+ private String desc;
+ EnumRWType(Integer value, String desc){
+ this.value=value;
+ this.desc=desc;
+ }
+ public static EnumRWType getValue(Integer value) {
+ for (EnumRWType c : EnumRWType.values()) {
+ if (c.getValue().equals(value)) {
+ return c;
+ }
+ }
+ return null;
+ }
+ public Integer getValue() {
+ return value;
+ }
+
+ public EnumRWType setValue(int value) {
+ this.value = value;
+ return this;
+
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+ }
+
+}
diff --git a/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/core/AbstractJdbcCore.java b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/core/AbstractJdbcCore.java
new file mode 100644
index 0000000000000000000000000000000000000000..c656adfef4509e45f77ca9f804f3ab1366bdc8d2
--- /dev/null
+++ b/mybatis-mini/src/main/java/com/vonchange/jdbc/abstractjdbc/core/AbstractJdbcCore.java
@@ -0,0 +1,847 @@
+package com.vonchange.jdbc.abstractjdbc.core;
+
+
+import com.vonchange.common.util.ConvertUtil;
+import com.vonchange.common.util.StringUtils;
+import com.vonchange.jdbc.abstractjdbc.config.ConstantJdbc;
+import com.vonchange.jdbc.abstractjdbc.config.Constants;
+import com.vonchange.jdbc.abstractjdbc.count.CountSqlParser;
+import com.vonchange.jdbc.abstractjdbc.handler.AbstractMapPageWork;
+import com.vonchange.jdbc.abstractjdbc.handler.AbstractPageWork;
+import com.vonchange.jdbc.abstractjdbc.handler.BeanHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.BeanInsertHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.BeanListHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.BigDataBeanListHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.BigDataMapListHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.MapBeanListHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.MapHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.MapListHandler;
+import com.vonchange.jdbc.abstractjdbc.handler.ScalarHandler;
+import com.vonchange.jdbc.abstractjdbc.model.DataSourceWrapper;
+import com.vonchange.jdbc.abstractjdbc.model.EntityCu;
+import com.vonchange.jdbc.abstractjdbc.model.EntityInsertResult;
+import com.vonchange.jdbc.abstractjdbc.model.EntityUpdateResult;
+import com.vonchange.jdbc.abstractjdbc.template.MyJdbcTemplate;
+import com.vonchange.jdbc.abstractjdbc.util.ConvertMap;
+import com.vonchange.jdbc.abstractjdbc.util.markdown.MarkdownUtil;
+import com.vonchange.jdbc.abstractjdbc.util.markdown.bean.SqlInfo;
+import com.vonchange.jdbc.abstractjdbc.util.sql.SqlFill;
+import com.vonchange.mybatis.config.Constant;
+import com.vonchange.mybatis.dialect.Dialect;
+import com.vonchange.mybatis.exception.MybatisMinRuntimeException;
+import com.vonchange.mybatis.tpl.EntityUtil;
+import com.vonchange.mybatis.tpl.MybatisTpl;
+import com.vonchange.mybatis.tpl.model.EntityField;
+import com.vonchange.mybatis.tpl.model.EntityInfo;
+import com.vonchange.mybatis.tpl.model.SqlWithParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+
+import java.beans.IntrospectionException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * jdbc core
+ * by von_change
+ */
+public abstract class AbstractJdbcCore implements JdbcRepository {
+
+ private static final Logger log = LoggerFactory.getLogger(AbstractJdbcCore.class);
+
+ protected abstract Dialect getDefaultDialect();
+
+ protected abstract DataSourceWrapper getWriteDataSource();
+
+
+ protected abstract boolean readAllScopeOpen();
+
+ protected abstract int batchSize();
+
+
+ protected abstract DataSourceWrapper getDataSourceFromSql(String sql);
+ protected abstract boolean logRead();
+ protected abstract boolean logWrite();
+ protected abstract boolean logFullSql();
+ protected abstract boolean needReadMdLastModified();
+
+ private Dialect dialect = null;
+ private Dialect getDialect(DataSourceWrapper dataSourceWrapper){
+ if(null!=dataSourceWrapper&&null!=dataSourceWrapper.getDialect()){
+ return dataSourceWrapper.getDialect();
+ }
+ if (null == dialect) {
+ dialect = getDefaultDialect();
+ }
+ return dialect;
+ }
+ private MyJdbcTemplate initJdbcTemplate(DataSourceWrapper dataSourceWrapper, Constants.EnumRWType enumRWType, String sql) {
+ return getJdbcTemplate(dataSourceWrapper, enumRWType, sql);
+ }
+
+ private static Map yhJdbcTemplateMap = new ConcurrentHashMap<>();
+
+ private DataSourceWrapper getDataSourceWrapper(DataSourceWrapper dataSourceWrapper, Constants.EnumRWType enumRWType,
+ String sql) {
+ if (null != dataSourceWrapper) {
+ return dataSourceWrapper;
+ }
+ //from sql get datasource
+ DataSourceWrapper dataSourceFromSql = getDataSourceFromSql(sql);
+ if (null != dataSourceFromSql) {
+ return dataSourceFromSql;
+ }
+ //去除 直接读随机数据源 主从有延迟 还是需要根据业务指定数据源
+ if (enumRWType.equals(Constants.EnumRWType.read) && readAllScopeOpen()) {
+ return getReadDataSource();
+ }
+ return getWriteDataSource();
+ }
+
+ private MyJdbcTemplate getJdbcTemplate(DataSourceWrapper dataSourceWrapper, Constants.EnumRWType enumRWType, String sql) {
+ DataSourceWrapper dataSource = getDataSourceWrapper(dataSourceWrapper, enumRWType, sql);
+ log.debug("\n====== use dataSource key {}", dataSource.getKey());
+ if (yhJdbcTemplateMap.containsKey(dataSource.getKey())) {
+ return yhJdbcTemplateMap.get(dataSource.getKey());
+ }
+ MyJdbcTemplate myJdbcTemplate = new MyJdbcTemplate(dataSource.getDataSource());
+ myJdbcTemplate.setFetchSizeBigData(getDialect(dataSource).getBigDataFetchSize());
+ myJdbcTemplate.setFetchSize(getDialect(dataSource).getFetchSize());
+ yhJdbcTemplateMap.put(dataSource.getKey(), myJdbcTemplate);
+ return myJdbcTemplate;
+ }
+
+ public final int insertBatch(List entityList,int batchSize) {
+ return insertBatch(null, entityList,batchSize);
+ }
+
+ public final int insertBatch(DataSourceWrapper dataSourceWrapper, List entityList,int batchSize) {
+ return insertBatch(dataSourceWrapper, entityList, false,batchSize);
+ }
+
+ public final int insertBatchDuplicateKey(List entityList,int batchSize) {
+ return insertBatchDuplicateKey(null, entityList,batchSize);
+ }
+
+ public final int insertBatchDuplicateKey(DataSourceWrapper dataSourceWrapper, List entityList,int batchSize) {
+ return insertBatch(dataSourceWrapper, entityList, true,batchSize);
+ }
+ public final int updateBatch( List entityList, int batchSize) {
+ return updateBatch(entityList,batchSize);
+ }
+ public final int updateBatch(DataSourceWrapper dataSourceWrapper, List entityList, int batchSize) {
+ if (null == entityList || entityList.isEmpty()) {
+ return 0;
+ }
+ SqlWithParam sqlParmeter = generateUpdateEntitySql(entityList.get(0), false);
+ String sql = sqlParmeter.getSql();
+ List