A[] addObjectToArray(A[] array, O obj) {
+ Class> compType = Object.class;
+ if (array != null) {
+ compType = array.getClass().getComponentType();
+ }
+ else if (obj != null) {
+ compType = obj.getClass();
+ }
+ int newArrLength = (array != null ? array.length + 1 : 1);
+ @SuppressWarnings("unchecked")
+ A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
+ if (array != null) {
+ System.arraycopy(array, 0, newArr, 0, array.length);
+ }
+ newArr[newArr.length - 1] = obj;
+ return newArr;
+ }
+
+ /**
+ * Convert the given array (which may be a primitive array) to an
+ * object array (if necessary of primitive wrapper objects).
+ * A {@code null} source value will be converted to an
+ * empty Object array.
+ * @param source the (potentially primitive) array
+ * @return the corresponding object array (never {@code null})
+ * @throws IllegalArgumentException if the parameter is not an array
+ */
+ public static Object[] toObjectArray(Object source) {
+ if (source instanceof Object[]) {
+ return (Object[]) source;
+ }
+ if (source == null) {
+ return new Object[0];
+ }
+ if (!source.getClass().isArray()) {
+ throw new IllegalArgumentException("Source is not an array: " + source);
+ }
+ int length = Array.getLength(source);
+ if (length == 0) {
+ return new Object[0];
+ }
+ Class> wrapperType = Array.get(source, 0).getClass();
+ Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
+ for (int i = 0; i < length; i++) {
+ newArray[i] = Array.get(source, i);
+ }
+ return newArray;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for content-based equality/hash-code handling
+ //---------------------------------------------------------------------
+
+ /**
+ * Determine if the given objects are equal, returning {@code true} if
+ * both are {@code null} or {@code false} if only one is {@code null}.
+ *
Compares arrays with {@code Arrays.equals}, performing an equality
+ * check based on the array elements rather than the array reference.
+ * @param o1 first Object to compare
+ * @param o2 second Object to compare
+ * @return whether the given objects are equal
+ * @see Object#equals(Object)
+ * @see Arrays#equals
+ */
+ public static boolean nullSafeEquals(Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ }
+ if (o1 == null || o2 == null) {
+ return false;
+ }
+ if (o1.equals(o2)) {
+ return true;
+ }
+ if (o1.getClass().isArray() && o2.getClass().isArray()) {
+ return arrayEquals(o1, o2);
+ }
+ return false;
+ }
+
+ /**
+ * Compare the given arrays with {@code Arrays.equals}, performing an equality
+ * check based on the array elements rather than the array reference.
+ * @param o1 first array to compare
+ * @param o2 second array to compare
+ * @return whether the given objects are equal
+ * @see #nullSafeEquals(Object, Object)
+ * @see Arrays#equals
+ */
+ private static boolean arrayEquals(Object o1, Object o2) {
+ if (o1 instanceof Object[] && o2 instanceof Object[]) {
+ return Arrays.equals((Object[]) o1, (Object[]) o2);
+ }
+ if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+ return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+ }
+ if (o1 instanceof byte[] && o2 instanceof byte[]) {
+ return Arrays.equals((byte[]) o1, (byte[]) o2);
+ }
+ if (o1 instanceof char[] && o2 instanceof char[]) {
+ return Arrays.equals((char[]) o1, (char[]) o2);
+ }
+ if (o1 instanceof double[] && o2 instanceof double[]) {
+ return Arrays.equals((double[]) o1, (double[]) o2);
+ }
+ if (o1 instanceof float[] && o2 instanceof float[]) {
+ return Arrays.equals((float[]) o1, (float[]) o2);
+ }
+ if (o1 instanceof int[] && o2 instanceof int[]) {
+ return Arrays.equals((int[]) o1, (int[]) o2);
+ }
+ if (o1 instanceof long[] && o2 instanceof long[]) {
+ return Arrays.equals((long[]) o1, (long[]) o2);
+ }
+ if (o1 instanceof short[] && o2 instanceof short[]) {
+ return Arrays.equals((short[]) o1, (short[]) o2);
+ }
+ return false;
+ }
+
+ /**
+ * Return as hash code for the given object; typically the value of
+ * {@code Object#hashCode()}}. If the object is an array,
+ * this method will delegate to any of the {@code nullSafeHashCode}
+ * methods for arrays in this class. If the object is {@code null},
+ * this method returns 0.
+ * @see Object#hashCode()
+ * @see #nullSafeHashCode(Object[])
+ * @see #nullSafeHashCode(boolean[])
+ * @see #nullSafeHashCode(byte[])
+ * @see #nullSafeHashCode(char[])
+ * @see #nullSafeHashCode(double[])
+ * @see #nullSafeHashCode(float[])
+ * @see #nullSafeHashCode(int[])
+ * @see #nullSafeHashCode(long[])
+ * @see #nullSafeHashCode(short[])
+ */
+ public static int nullSafeHashCode(Object obj) {
+ if (obj == null) {
+ return 0;
+ }
+ if (obj.getClass().isArray()) {
+ if (obj instanceof Object[]) {
+ return nullSafeHashCode((Object[]) obj);
+ }
+ if (obj instanceof boolean[]) {
+ return nullSafeHashCode((boolean[]) obj);
+ }
+ if (obj instanceof byte[]) {
+ return nullSafeHashCode((byte[]) obj);
+ }
+ if (obj instanceof char[]) {
+ return nullSafeHashCode((char[]) obj);
+ }
+ if (obj instanceof double[]) {
+ return nullSafeHashCode((double[]) obj);
+ }
+ if (obj instanceof float[]) {
+ return nullSafeHashCode((float[]) obj);
+ }
+ if (obj instanceof int[]) {
+ return nullSafeHashCode((int[]) obj);
+ }
+ if (obj instanceof long[]) {
+ return nullSafeHashCode((long[]) obj);
+ }
+ if (obj instanceof short[]) {
+ return nullSafeHashCode((short[]) obj);
+ }
+ }
+ return obj.hashCode();
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(Object[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (Object element : array) {
+ hash = MULTIPLIER * hash + nullSafeHashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(boolean[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (boolean element : array) {
+ hash = MULTIPLIER * hash + hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(byte[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (byte element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(char[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (char element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(double[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (double element : array) {
+ hash = MULTIPLIER * hash + hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(float[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (float element : array) {
+ hash = MULTIPLIER * hash + hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(int[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (int element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(long[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (long element : array) {
+ hash = MULTIPLIER * hash + hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(short[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (short element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return the same value as {@link Boolean#hashCode()}}.
+ * @see Boolean#hashCode()
+ */
+ public static int hashCode(boolean bool) {
+ return (bool ? 1231 : 1237);
+ }
+
+ /**
+ * Return the same value as {@link Double#hashCode()}}.
+ * @see Double#hashCode()
+ */
+ public static int hashCode(double dbl) {
+ return hashCode(Double.doubleToLongBits(dbl));
+ }
+
+ /**
+ * Return the same value as {@link Float#hashCode()}}.
+ * @see Float#hashCode()
+ */
+ public static int hashCode(float flt) {
+ return Float.floatToIntBits(flt);
+ }
+
+ /**
+ * Return the same value as {@link Long#hashCode()}}.
+ * @see Long#hashCode()
+ */
+ public static int hashCode(long lng) {
+ return (int) (lng ^ (lng >>> 32));
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for toString output
+ //---------------------------------------------------------------------
+
+ /**
+ * Return a String representation of an object's overall identity.
+ * @param obj the object (may be {@code null})
+ * @return the object's identity as String representation,
+ * or an empty String if the object was {@code null}
+ */
+ public static String identityToString(Object obj) {
+ if (obj == null) {
+ return EMPTY_STRING;
+ }
+ return obj.getClass().getName() + "@" + getIdentityHexString(obj);
+ }
+
+ /**
+ * Return a hex String form of an object's identity hash code.
+ * @param obj the object
+ * @return the object's identity code in hex notation
+ */
+ public static String getIdentityHexString(Object obj) {
+ return Integer.toHexString(System.identityHashCode(obj));
+ }
+
+ /**
+ * Return a content-based String representation if {@code obj} is
+ * not {@code null}; otherwise returns an empty String.
+ *
Differs from {@link #nullSafeToString(Object)} in that it returns
+ * an empty String rather than "null" for a {@code null} value.
+ * @param obj the object to build a display String for
+ * @return a display String representation of {@code obj}
+ * @see #nullSafeToString(Object)
+ */
+ public static String getDisplayString(Object obj) {
+ if (obj == null) {
+ return EMPTY_STRING;
+ }
+ return nullSafeToString(obj);
+ }
+
+ /**
+ * Determine the class name for the given object.
+ *
Returns {@code "null"} if {@code obj} is {@code null}.
+ * @param obj the object to introspect (may be {@code null})
+ * @return the corresponding class name
+ */
+ public static String nullSafeClassName(Object obj) {
+ return (obj != null ? obj.getClass().getName() : NULL_STRING);
+ }
+
+ /**
+ * Return a String representation of the specified Object.
+ *
Builds a String representation of the contents in case of an array.
+ * Returns {@code "null"} if {@code obj} is {@code null}.
+ * @param obj the object to build a String representation for
+ * @return a String representation of {@code obj}
+ */
+ public static String nullSafeToString(Object obj) {
+ if (obj == null) {
+ return NULL_STRING;
+ }
+ if (obj instanceof String) {
+ return (String) obj;
+ }
+ if (obj instanceof Object[]) {
+ return nullSafeToString((Object[]) obj);
+ }
+ if (obj instanceof boolean[]) {
+ return nullSafeToString((boolean[]) obj);
+ }
+ if (obj instanceof byte[]) {
+ return nullSafeToString((byte[]) obj);
+ }
+ if (obj instanceof char[]) {
+ return nullSafeToString((char[]) obj);
+ }
+ if (obj instanceof double[]) {
+ return nullSafeToString((double[]) obj);
+ }
+ if (obj instanceof float[]) {
+ return nullSafeToString((float[]) obj);
+ }
+ if (obj instanceof int[]) {
+ return nullSafeToString((int[]) obj);
+ }
+ if (obj instanceof long[]) {
+ return nullSafeToString((long[]) obj);
+ }
+ if (obj instanceof short[]) {
+ return nullSafeToString((short[]) obj);
+ }
+ String str = obj.toString();
+ return (str != null ? str : EMPTY_STRING);
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(Object[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append(String.valueOf(array[i]));
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(boolean[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(byte[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(char[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append("'").append(array[i]).append("'");
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(double[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(float[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(int[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(long[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ *
The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space). Returns
+ * {@code "null"} if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(short[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length; i++) {
+ if (i == 0) {
+ sb.append(ARRAY_START);
+ }
+ else {
+ sb.append(ARRAY_ELEMENT_SEPARATOR);
+ }
+ sb.append(array[i]);
+ }
+ sb.append(ARRAY_END);
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/com/alibaba/excel/util/POITempFile.java b/src/main/java/com/alibaba/excel/util/POITempFile.java
index 6382255f..c342968a 100644
--- a/src/main/java/com/alibaba/excel/util/POITempFile.java
+++ b/src/main/java/com/alibaba/excel/util/POITempFile.java
@@ -5,6 +5,7 @@ import java.io.File;
/**
*
* @author jipengfei
+ * @date 2017/06/22
*/
public class POITempFile {
diff --git a/src/main/java/com/alibaba/excel/util/PositionUtils.java b/src/main/java/com/alibaba/excel/util/PositionUtils.java
index 37a87dd5..f5f084ac 100644
--- a/src/main/java/com/alibaba/excel/util/PositionUtils.java
+++ b/src/main/java/com/alibaba/excel/util/PositionUtils.java
@@ -2,6 +2,7 @@ package com.alibaba.excel.util;
/**
* @author jipengfei
+ * @date 2017/08/27
*/
public class PositionUtils {
diff --git a/src/main/java/com/alibaba/excel/util/RowUtil.java b/src/main/java/com/alibaba/excel/util/RowUtil.java
new file mode 100644
index 00000000..dc5ea013
--- /dev/null
+++ b/src/main/java/com/alibaba/excel/util/RowUtil.java
@@ -0,0 +1,10 @@
+package com.alibaba.excel.util;
+
+import org.apache.poi.ss.usermodel.Workbook;
+
+public class RowUtil {
+
+ //public static int computeNextRow(Workbook workbook,int startRow){
+ //
+ //}
+}
diff --git a/src/main/java/com/alibaba/excel/util/StringUtils.java b/src/main/java/com/alibaba/excel/util/StringUtils.java
new file mode 100644
index 00000000..bd7e12f6
--- /dev/null
+++ b/src/main/java/com/alibaba/excel/util/StringUtils.java
@@ -0,0 +1,1221 @@
+package com.alibaba.excel.util;
+
+/*
+ * 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.
+ */
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+/**
+ * Miscellaneous {@link String} utility methods.
+ *
+ *
Mainly for internal use within the framework; consider
+ * Apache's Commons Lang
+ * for a more comprehensive suite of {@code String} utilities.
+ *
+ *
This class delivers some simple functionality that should really be
+ * provided by the core Java {@link String} and {@link StringBuilder}
+ * classes. It also provides easy-to-use methods to convert between
+ * delimited strings, such as CSV strings, and collections and arrays.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rob Harrop
+ * @author Rick Evans
+ * @author Arjen Poutsma
+ * @author Sam Brannen
+ * @author Brian Clozel
+ * @since 16 April 2001
+ */
+public abstract class StringUtils {
+
+ private static final String FOLDER_SEPARATOR = "/";
+
+ private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
+
+ private static final String TOP_PATH = "..";
+
+ private static final String CURRENT_PATH = ".";
+
+ private static final char EXTENSION_SEPARATOR = '.';
+
+
+ //---------------------------------------------------------------------
+ // General convenience methods for working with Strings
+ //---------------------------------------------------------------------
+
+ /**
+ * Check whether the given {@code String} is empty.
+ *
This method accepts any Object as an argument, comparing it to
+ * {@code null} and the empty String. As a consequence, this method
+ * will never return {@code true} for a non-null non-String object.
+ *
The Object signature is useful for general attribute handling code
+ * that commonly deals with Strings but generally has to iterate over
+ * Objects since attributes may e.g. be primitive value objects as well.
+ * @param str the candidate String
+ * @since 3.2.1
+ */
+ public static boolean isEmpty(Object str) {
+ return (str == null || "".equals(str));
+ }
+
+ /**
+ * Check that the given {@code CharSequence} is neither {@code null} nor
+ * of length 0.
+ *
Note: this method returns {@code true} for a {@code CharSequence}
+ * that purely consists of whitespace.
+ *
+ * StringUtils.hasLength(null) = false
+ * StringUtils.hasLength("") = false
+ * StringUtils.hasLength(" ") = true
+ * StringUtils.hasLength("Hello") = true
+ *
+ * @param str the {@code CharSequence} to check (may be {@code null})
+ * @return {@code true} if the {@code CharSequence} is not {@code null} and has length
+ * @see #hasText(String)
+ */
+ public static boolean hasLength(CharSequence str) {
+ return (str != null && str.length() > 0);
+ }
+
+ /**
+ * Check that the given {@code String} is neither {@code null} nor of length 0.
+ * Note: this method returns {@code true} for a {@code String} that
+ * purely consists of whitespace.
+ * @param str the {@code String} to check (may be {@code null})
+ * @return {@code true} if the {@code String} is not {@code null} and has length
+ * @see #hasLength(CharSequence)
+ * @see #hasText(String)
+ */
+ public static boolean hasLength(String str) {
+ return (str != null && !str.isEmpty());
+ }
+
+ /**
+ * Check whether the given {@code CharSequence} contains actual text.
+ *
More specifically, this method returns {@code true} if the
+ * {@code CharSequence} is not {@code null}, its length is greater than
+ * 0, and it contains at least one non-whitespace character.
+ *
+ * StringUtils.hasText(null) = false
+ * StringUtils.hasText("") = false
+ * StringUtils.hasText(" ") = false
+ * StringUtils.hasText("12345") = true
+ * StringUtils.hasText(" 12345 ") = true
+ *
+ * @param str the {@code CharSequence} to check (may be {@code null})
+ * @return {@code true} if the {@code CharSequence} is not {@code null},
+ * its length is greater than 0, and it does not contain whitespace only
+ * @see Character#isWhitespace
+ */
+ public static boolean hasText(CharSequence str) {
+ return (hasLength(str) && containsText(str));
+ }
+
+ /**
+ * Check whether the given {@code String} contains actual text.
+ * More specifically, this method returns {@code true} if the
+ * {@code String} is not {@code null}, its length is greater than 0,
+ * and it contains at least one non-whitespace character.
+ * @param str the {@code String} to check (may be {@code null})
+ * @return {@code true} if the {@code String} is not {@code null}, its
+ * length is greater than 0, and it does not contain whitespace only
+ * @see #hasText(CharSequence)
+ */
+ public static boolean hasText(String str) {
+ return (hasLength(str) && containsText(str));
+ }
+
+ private static boolean containsText(CharSequence str) {
+ int strLen = str.length();
+ for (int i = 0; i < strLen; i++) {
+ if (!Character.isWhitespace(str.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the given {@code CharSequence} contains any whitespace characters.
+ * @param str the {@code CharSequence} to check (may be {@code null})
+ * @return {@code true} if the {@code CharSequence} is not empty and
+ * contains at least 1 whitespace character
+ * @see Character#isWhitespace
+ */
+ public static boolean containsWhitespace(CharSequence str) {
+ if (!hasLength(str)) {
+ return false;
+ }
+
+ int strLen = str.length();
+ for (int i = 0; i < strLen; i++) {
+ if (Character.isWhitespace(str.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the given {@code String} contains any whitespace characters.
+ * @param str the {@code String} to check (may be {@code null})
+ * @return {@code true} if the {@code String} is not empty and
+ * contains at least 1 whitespace character
+ * @see #containsWhitespace(CharSequence)
+ */
+ public static boolean containsWhitespace(String str) {
+ return containsWhitespace((CharSequence) str);
+ }
+
+ /**
+ * Trim leading and trailing whitespace from the given {@code String}.
+ * @param str the {@code String} to check
+ * @return the trimmed {@code String}
+ * @see Character#isWhitespace
+ */
+ public static String trimWhitespace(String str) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(str);
+ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
+ sb.deleteCharAt(0);
+ }
+ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Trim all whitespace from the given {@code String}:
+ * leading, trailing, and in between characters.
+ * @param str the {@code String} to check
+ * @return the trimmed {@code String}
+ * @see Character#isWhitespace
+ */
+ public static String trimAllWhitespace(String str) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ int len = str.length();
+ StringBuilder sb = new StringBuilder(str.length());
+ for (int i = 0; i < len; i++) {
+ char c = str.charAt(i);
+ if (!Character.isWhitespace(c)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Trim leading whitespace from the given {@code String}.
+ * @param str the {@code String} to check
+ * @return the trimmed {@code String}
+ * @see Character#isWhitespace
+ */
+ public static String trimLeadingWhitespace(String str) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(str);
+ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
+ sb.deleteCharAt(0);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Trim trailing whitespace from the given {@code String}.
+ * @param str the {@code String} to check
+ * @return the trimmed {@code String}
+ * @see Character#isWhitespace
+ */
+ public static String trimTrailingWhitespace(String str) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(str);
+ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Trim all occurrences of the supplied leading character from the given {@code String}.
+ * @param str the {@code String} to check
+ * @param leadingCharacter the leading character to be trimmed
+ * @return the trimmed {@code String}
+ */
+ public static String trimLeadingCharacter(String str, char leadingCharacter) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(str);
+ while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {
+ sb.deleteCharAt(0);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Trim all occurrences of the supplied trailing character from the given {@code String}.
+ * @param str the {@code String} to check
+ * @param trailingCharacter the trailing character to be trimmed
+ * @return the trimmed {@code String}
+ */
+ public static String trimTrailingCharacter(String str, char trailingCharacter) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ StringBuilder sb = new StringBuilder(str);
+ while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Test if the given {@code String} starts with the specified prefix,
+ * ignoring upper/lower case.
+ * @param str the {@code String} to check
+ * @param prefix the prefix to look for
+ * @see String#startsWith
+ */
+ public static boolean startsWithIgnoreCase(String str, String prefix) {
+ return (str != null && prefix != null && str.length() >= prefix.length() &&
+ str.regionMatches(true, 0, prefix, 0, prefix.length()));
+ }
+
+ /**
+ * Test if the given {@code String} ends with the specified suffix,
+ * ignoring upper/lower case.
+ * @param str the {@code String} to check
+ * @param suffix the suffix to look for
+ * @see String#endsWith
+ */
+ public static boolean endsWithIgnoreCase(String str, String suffix) {
+ return (str != null && suffix != null && str.length() >= suffix.length() &&
+ str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length()));
+ }
+
+ /**
+ * Test whether the given string matches the given substring
+ * at the given index.
+ * @param str the original string (or StringBuilder)
+ * @param index the index in the original string to start matching against
+ * @param substring the substring to match at the given index
+ */
+ public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
+ if (index + substring.length() > str.length()) {
+ return false;
+ }
+ for (int i = 0; i < substring.length(); i++) {
+ if (str.charAt(index + i) != substring.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Count the occurrences of the substring {@code sub} in string {@code str}.
+ * @param str string to search in
+ * @param sub string to search for
+ */
+ public static int countOccurrencesOf(String str, String sub) {
+ if (!hasLength(str) || !hasLength(sub)) {
+ return 0;
+ }
+
+ int count = 0;
+ int pos = 0;
+ int idx;
+ while ((idx = str.indexOf(sub, pos)) != -1) {
+ ++count;
+ pos = idx + sub.length();
+ }
+ return count;
+ }
+
+ /**
+ * Replace all occurrences of a substring within a string with another string.
+ * @param inString {@code String} to examine
+ * @param oldPattern {@code String} to replace
+ * @param newPattern {@code String} to insert
+ * @return a {@code String} with the replacements
+ */
+ public static String replace(String inString, String oldPattern, String newPattern) {
+ if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
+ return inString;
+ }
+ int index = inString.indexOf(oldPattern);
+ if (index == -1) {
+ // no occurrence -> can return input as-is
+ return inString;
+ }
+
+ int capacity = inString.length();
+ if (newPattern.length() > oldPattern.length()) {
+ capacity += 16;
+ }
+ StringBuilder sb = new StringBuilder(capacity);
+
+ int pos = 0; // our position in the old string
+ int patLen = oldPattern.length();
+ while (index >= 0) {
+ sb.append(inString.substring(pos, index));
+ sb.append(newPattern);
+ pos = index + patLen;
+ index = inString.indexOf(oldPattern, pos);
+ }
+
+ // append any characters to the right of a match
+ sb.append(inString.substring(pos));
+ return sb.toString();
+ }
+
+ /**
+ * Delete all occurrences of the given substring.
+ * @param inString the original {@code String}
+ * @param pattern the pattern to delete all occurrences of
+ * @return the resulting {@code String}
+ */
+ public static String delete(String inString, String pattern) {
+ return replace(inString, pattern, "");
+ }
+
+ /**
+ * Delete any character in a given {@code String}.
+ * @param inString the original {@code String}
+ * @param charsToDelete a set of characters to delete.
+ * E.g. "az\n" will delete 'a's, 'z's and new lines.
+ * @return the resulting {@code String}
+ */
+ public static String deleteAny(String inString, String charsToDelete) {
+ if (!hasLength(inString) || !hasLength(charsToDelete)) {
+ return inString;
+ }
+
+ StringBuilder sb = new StringBuilder(inString.length());
+ for (int i = 0; i < inString.length(); i++) {
+ char c = inString.charAt(i);
+ if (charsToDelete.indexOf(c) == -1) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for working with formatted Strings
+ //---------------------------------------------------------------------
+
+ /**
+ * Quote the given {@code String} with single quotes.
+ * @param str the input {@code String} (e.g. "myString")
+ * @return the quoted {@code String} (e.g. "'myString'"),
+ * or {@code null} if the input was {@code null}
+ */
+ public static String quote(String str) {
+ return (str != null ? "'" + str + "'" : null);
+ }
+
+ /**
+ * Turn the given Object into a {@code String} with single quotes
+ * if it is a {@code String}; keeping the Object as-is else.
+ * @param obj the input Object (e.g. "myString")
+ * @return the quoted {@code String} (e.g. "'myString'"),
+ * or the input object as-is if not a {@code String}
+ */
+ public static Object quoteIfString(Object obj) {
+ return (obj instanceof String ? quote((String) obj) : obj);
+ }
+
+ /**
+ * Unqualify a string qualified by a '.' dot character. For example,
+ * "this.name.is.qualified", returns "qualified".
+ * @param qualifiedName the qualified name
+ */
+ public static String unqualify(String qualifiedName) {
+ return unqualify(qualifiedName, '.');
+ }
+
+ /**
+ * Unqualify a string qualified by a separator character. For example,
+ * "this:name:is:qualified" returns "qualified" if using a ':' separator.
+ * @param qualifiedName the qualified name
+ * @param separator the separator
+ */
+ public static String unqualify(String qualifiedName, char separator) {
+ return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);
+ }
+
+ /**
+ * Capitalize a {@code String}, changing the first letter to
+ * upper case as per {@link Character#toUpperCase(char)}.
+ * No other letters are changed.
+ * @param str the {@code String} to capitalize
+ * @return the capitalized {@code String}
+ */
+ public static String capitalize(String str) {
+ return changeFirstCharacterCase(str, true);
+ }
+
+ /**
+ * Uncapitalize a {@code String}, changing the first letter to
+ * lower case as per {@link Character#toLowerCase(char)}.
+ * No other letters are changed.
+ * @param str the {@code String} to uncapitalize
+ * @return the uncapitalized {@code String}
+ */
+ public static String uncapitalize(String str) {
+ return changeFirstCharacterCase(str, false);
+ }
+
+ private static String changeFirstCharacterCase(String str, boolean capitalize) {
+ if (!hasLength(str)) {
+ return str;
+ }
+
+ char baseChar = str.charAt(0);
+ char updatedChar;
+ if (capitalize) {
+ updatedChar = Character.toUpperCase(baseChar);
+ }
+ else {
+ updatedChar = Character.toLowerCase(baseChar);
+ }
+ if (baseChar == updatedChar) {
+ return str;
+ }
+
+ char[] chars = str.toCharArray();
+ chars[0] = updatedChar;
+ return new String(chars, 0, chars.length);
+ }
+
+ /**
+ * Extract the filename from the given Java resource path,
+ * e.g. {@code "mypath/myfile.txt" -> "myfile.txt"}.
+ * @param path the file path (may be {@code null})
+ * @return the extracted filename, or {@code null} if none
+ */
+ public static String getFilename(String path) {
+ if (path == null) {
+ return null;
+ }
+
+ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
+ }
+
+ /**
+ * Extract the filename extension from the given Java resource path,
+ * e.g. "mypath/myfile.txt" -> "txt".
+ * @param path the file path (may be {@code null})
+ * @return the extracted filename extension, or {@code null} if none
+ */
+ public static String getFilenameExtension(String path) {
+ if (path == null) {
+ return null;
+ }
+
+ int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
+ if (extIndex == -1) {
+ return null;
+ }
+
+ int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ if (folderIndex > extIndex) {
+ return null;
+ }
+
+ return path.substring(extIndex + 1);
+ }
+
+ /**
+ * Strip the filename extension from the given Java resource path,
+ * e.g. "mypath/myfile.txt" -> "mypath/myfile".
+ * @param path the file path
+ * @return the path with stripped filename extension
+ */
+ public static String stripFilenameExtension(String path) {
+ if (path == null) {
+ return null;
+ }
+
+ int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
+ if (extIndex == -1) {
+ return path;
+ }
+
+ int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ if (folderIndex > extIndex) {
+ return path;
+ }
+
+ return path.substring(0, extIndex);
+ }
+
+ /**
+ * Apply the given relative path to the given Java resource path,
+ * assuming standard Java folder separation (i.e. "/" separators).
+ * @param path the path to start from (usually a full file path)
+ * @param relativePath the relative path to apply
+ * (relative to the full file path above)
+ * @return the full file path that results from applying the relative path
+ */
+ public static String applyRelativePath(String path, String relativePath) {
+ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
+ if (separatorIndex != -1) {
+ String newPath = path.substring(0, separatorIndex);
+ if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
+ newPath += FOLDER_SEPARATOR;
+ }
+ return newPath + relativePath;
+ }
+ else {
+ return relativePath;
+ }
+ }
+
+ /**
+ * Normalize the path by suppressing sequences like "path/.." and
+ * inner simple dots.
+ *
The result is convenient for path comparison. For other uses,
+ * notice that Windows separators ("\") are replaced by simple slashes.
+ * @param path the original path
+ * @return the normalized path
+ */
+ public static String cleanPath(String path) {
+ if (path == null) {
+ return null;
+ }
+ String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);
+
+ // Strip prefix from path to analyze, to not treat it as part of the
+ // first path element. This is necessary to correctly parse paths like
+ // "file:core/../core/io/Resource.class", where the ".." should just
+ // strip the first "core" directory while keeping the "file:" prefix.
+ int prefixIndex = pathToUse.indexOf(":");
+ String prefix = "";
+ if (prefixIndex != -1) {
+ prefix = pathToUse.substring(0, prefixIndex + 1);
+ if (prefix.contains("/")) {
+ prefix = "";
+ }
+ else {
+ pathToUse = pathToUse.substring(prefixIndex + 1);
+ }
+ }
+ if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
+ prefix = prefix + FOLDER_SEPARATOR;
+ pathToUse = pathToUse.substring(1);
+ }
+
+ String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
+ List pathElements = new LinkedList();
+ int tops = 0;
+
+ for (int i = pathArray.length - 1; i >= 0; i--) {
+ String element = pathArray[i];
+ if (CURRENT_PATH.equals(element)) {
+ // Points to current directory - drop it.
+ }
+ else if (TOP_PATH.equals(element)) {
+ // Registering top path found.
+ tops++;
+ }
+ else {
+ if (tops > 0) {
+ // Merging path element with element corresponding to top path.
+ tops--;
+ }
+ else {
+ // Normal path element found.
+ pathElements.add(0, element);
+ }
+ }
+ }
+
+ // Remaining top paths need to be retained.
+ for (int i = 0; i < tops; i++) {
+ pathElements.add(0, TOP_PATH);
+ }
+
+ return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
+ }
+
+ /**
+ * Compare two paths after normalization of them.
+ * @param path1 first path for comparison
+ * @param path2 second path for comparison
+ * @return whether the two paths are equivalent after normalization
+ */
+ public static boolean pathEquals(String path1, String path2) {
+ return cleanPath(path1).equals(cleanPath(path2));
+ }
+
+ /**
+ * Parse the given {@code localeString} value into a {@link Locale}.
+ * This is the inverse operation of {@link Locale#toString Locale's toString}.
+ * @param localeString the locale {@code String}, following {@code Locale's}
+ * {@code toString()} format ("en", "en_UK", etc);
+ * also accepts spaces as separators, as an alternative to underscores
+ * @return a corresponding {@code Locale} instance, or {@code null} if none
+ * @throws IllegalArgumentException in case of an invalid locale specification
+ */
+ public static Locale parseLocaleString(String localeString) {
+ String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);
+ String language = (parts.length > 0 ? parts[0] : "");
+ String country = (parts.length > 1 ? parts[1] : "");
+
+ validateLocalePart(language);
+ validateLocalePart(country);
+
+ String variant = "";
+ if (parts.length > 2) {
+ // There is definitely a variant, and it is everything after the country
+ // code sans the separator between the country code and the variant.
+ int endIndexOfCountryCode = localeString.indexOf(country, language.length()) + country.length();
+ // Strip off any leading '_' and whitespace, what's left is the variant.
+ variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));
+ if (variant.startsWith("_")) {
+ variant = trimLeadingCharacter(variant, '_');
+ }
+ }
+ return (language.length() > 0 ? new Locale(language, country, variant) : null);
+ }
+
+ private static void validateLocalePart(String localePart) {
+ for (int i = 0; i < localePart.length(); i++) {
+ char ch = localePart.charAt(i);
+ if (ch != ' ' && ch != '_' && ch != '#' && !Character.isLetterOrDigit(ch)) {
+ throw new IllegalArgumentException(
+ "Locale part \"" + localePart + "\" contains invalid characters");
+ }
+ }
+ }
+
+ /**
+ * Determine the RFC 3066 compliant language tag,
+ * as used for the HTTP "Accept-Language" header.
+ * @param locale the Locale to transform to a language tag
+ * @return the RFC 3066 compliant language tag as {@code String}
+ */
+ public static String toLanguageTag(Locale locale) {
+ return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");
+ }
+
+ /**
+ * Parse the given {@code timeZoneString} value into a {@link TimeZone}.
+ * @param timeZoneString the time zone {@code String}, following {@link TimeZone#getTimeZone(String)}
+ * but throwing {@link IllegalArgumentException} in case of an invalid time zone specification
+ * @return a corresponding {@link TimeZone} instance
+ * @throws IllegalArgumentException in case of an invalid time zone specification
+ */
+ public static TimeZone parseTimeZoneString(String timeZoneString) {
+ TimeZone timeZone = TimeZone.getTimeZone(timeZoneString);
+ if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) {
+ // We don't want that GMT fallback...
+ throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'");
+ }
+ return timeZone;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for working with String arrays
+ //---------------------------------------------------------------------
+
+ /**
+ * Append the given {@code String} to the given {@code String} array,
+ * returning a new array consisting of the input array contents plus
+ * the given {@code String}.
+ * @param array the array to append to (can be {@code null})
+ * @param str the {@code String} to append
+ * @return the new array (never {@code null})
+ */
+ public static String[] addStringToArray(String[] array, String str) {
+ if (ObjectUtils.isEmpty(array)) {
+ return new String[] {str};
+ }
+
+ String[] newArr = new String[array.length + 1];
+ System.arraycopy(array, 0, newArr, 0, array.length);
+ newArr[array.length] = str;
+ return newArr;
+ }
+
+ /**
+ * Concatenate the given {@code String} arrays into one,
+ * with overlapping array elements included twice.
+ *
The order of elements in the original arrays is preserved.
+ * @param array1 the first array (can be {@code null})
+ * @param array2 the second array (can be {@code null})
+ * @return the new array ({@code null} if both given arrays were {@code null})
+ */
+ public static String[] concatenateStringArrays(String[] array1, String[] array2) {
+ if (ObjectUtils.isEmpty(array1)) {
+ return array2;
+ }
+ if (ObjectUtils.isEmpty(array2)) {
+ return array1;
+ }
+
+ String[] newArr = new String[array1.length + array2.length];
+ System.arraycopy(array1, 0, newArr, 0, array1.length);
+ System.arraycopy(array2, 0, newArr, array1.length, array2.length);
+ return newArr;
+ }
+
+ /**
+ * Merge the given {@code String} arrays into one, with overlapping
+ * array elements only included once.
+ *
The order of elements in the original arrays is preserved
+ * (with the exception of overlapping elements, which are only
+ * included on their first occurrence).
+ * @param array1 the first array (can be {@code null})
+ * @param array2 the second array (can be {@code null})
+ * @return the new array ({@code null} if both given arrays were {@code null})
+ */
+ public static String[] mergeStringArrays(String[] array1, String[] array2) {
+ if (ObjectUtils.isEmpty(array1)) {
+ return array2;
+ }
+ if (ObjectUtils.isEmpty(array2)) {
+ return array1;
+ }
+
+ List result = new ArrayList();
+ result.addAll(Arrays.asList(array1));
+ for (String str : array2) {
+ if (!result.contains(str)) {
+ result.add(str);
+ }
+ }
+ return toStringArray(result);
+ }
+
+ /**
+ * Turn given source {@code String} array into sorted array.
+ * @param array the source array
+ * @return the sorted array (never {@code null})
+ */
+ public static String[] sortStringArray(String[] array) {
+ if (ObjectUtils.isEmpty(array)) {
+ return new String[0];
+ }
+
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Copy the given {@code Collection} into a {@code String} array.
+ * The {@code Collection} must contain {@code String} elements only.
+ * @param collection the {@code Collection} to copy
+ * @return the {@code String} array
+ */
+ public static String[] toStringArray(Collection collection) {
+ if (collection == null) {
+ return null;
+ }
+
+ return collection.toArray(new String[collection.size()]);
+ }
+
+ /**
+ * Copy the given Enumeration into a {@code String} array.
+ * The Enumeration must contain {@code String} elements only.
+ * @param enumeration the Enumeration to copy
+ * @return the {@code String} array
+ */
+ public static String[] toStringArray(Enumeration enumeration) {
+ if (enumeration == null) {
+ return null;
+ }
+
+ List list = Collections.list(enumeration);
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Trim the elements of the given {@code String} array,
+ * calling {@code String.trim()} on each of them.
+ * @param array the original {@code String} array
+ * @return the resulting array (of the same size) with trimmed elements
+ */
+ public static String[] trimArrayElements(String[] array) {
+ if (ObjectUtils.isEmpty(array)) {
+ return new String[0];
+ }
+
+ String[] result = new String[array.length];
+ for (int i = 0; i < array.length; i++) {
+ String element = array[i];
+ result[i] = (element != null ? element.trim() : null);
+ }
+ return result;
+ }
+
+ /**
+ * Remove duplicate strings from the given array.
+ * As of 4.2, it preserves the original order, as it uses a {@link LinkedHashSet}.
+ * @param array the {@code String} array
+ * @return an array without duplicates, in natural sort order
+ */
+ public static String[] removeDuplicateStrings(String[] array) {
+ if (ObjectUtils.isEmpty(array)) {
+ return array;
+ }
+
+ Set set = new LinkedHashSet();
+ for (String element : array) {
+ set.add(element);
+ }
+ return toStringArray(set);
+ }
+
+ /**
+ * Split a {@code String} at the first occurrence of the delimiter.
+ * Does not include the delimiter in the result.
+ * @param toSplit the string to split
+ * @param delimiter to split the string up with
+ * @return a two element array with index 0 being before the delimiter, and
+ * index 1 being after the delimiter (neither element includes the delimiter);
+ * or {@code null} if the delimiter wasn't found in the given input {@code String}
+ */
+ public static String[] split(String toSplit, String delimiter) {
+ if (!hasLength(toSplit) || !hasLength(delimiter)) {
+ return null;
+ }
+ int offset = toSplit.indexOf(delimiter);
+ if (offset < 0) {
+ return null;
+ }
+
+ String beforeDelimiter = toSplit.substring(0, offset);
+ String afterDelimiter = toSplit.substring(offset + delimiter.length());
+ return new String[] {beforeDelimiter, afterDelimiter};
+ }
+
+ /**
+ * Take an array of strings and split each element based on the given delimiter.
+ * A {@code Properties} instance is then generated, with the left of the
+ * delimiter providing the key, and the right of the delimiter providing the value.
+ * Will trim both the key and value before adding them to the
+ * {@code Properties} instance.
+ * @param array the array to process
+ * @param delimiter to split each element using (typically the equals symbol)
+ * @return a {@code Properties} instance representing the array contents,
+ * or {@code null} if the array to process was {@code null} or empty
+ */
+ public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
+ return splitArrayElementsIntoProperties(array, delimiter, null);
+ }
+
+ /**
+ * Take an array of strings and split each element based on the given delimiter.
+ * A {@code Properties} instance is then generated, with the left of the
+ * delimiter providing the key, and the right of the delimiter providing the value.
+ *
Will trim both the key and value before adding them to the
+ * {@code Properties} instance.
+ * @param array the array to process
+ * @param delimiter to split each element using (typically the equals symbol)
+ * @param charsToDelete one or more characters to remove from each element
+ * prior to attempting the split operation (typically the quotation mark
+ * symbol), or {@code null} if no removal should occur
+ * @return a {@code Properties} instance representing the array contents,
+ * or {@code null} if the array to process was {@code null} or empty
+ */
+ public static Properties splitArrayElementsIntoProperties(
+ String[] array, String delimiter, String charsToDelete) {
+
+ if (ObjectUtils.isEmpty(array)) {
+ return null;
+ }
+
+ Properties result = new Properties();
+ for (String element : array) {
+ if (charsToDelete != null) {
+ element = deleteAny(element, charsToDelete);
+ }
+ String[] splittedElement = split(element, delimiter);
+ if (splittedElement == null) {
+ continue;
+ }
+ result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());
+ }
+ return result;
+ }
+
+ /**
+ * Tokenize the given {@code String} into a {@code String} array via a
+ * {@link StringTokenizer}.
+ *
Trims tokens and omits empty tokens.
+ *
The given {@code delimiters} string can consist of any number of
+ * delimiter characters. Each of those characters can be used to separate
+ * tokens. A delimiter is always a single character; for multi-character
+ * delimiters, consider using {@link #delimitedListToStringArray}.
+ * @param str the {@code String} to tokenize
+ * @param delimiters the delimiter characters, assembled as a {@code String}
+ * (each of the characters is individually considered as a delimiter)
+ * @return an array of the tokens
+ * @see StringTokenizer
+ * @see String#trim()
+ * @see #delimitedListToStringArray
+ */
+ public static String[] tokenizeToStringArray(String str, String delimiters) {
+ return tokenizeToStringArray(str, delimiters, true, true);
+ }
+
+ /**
+ * Tokenize the given {@code String} into a {@code String} array via a
+ * {@link StringTokenizer}.
+ *
The given {@code delimiters} string can consist of any number of
+ * delimiter characters. Each of those characters can be used to separate
+ * tokens. A delimiter is always a single character; for multi-character
+ * delimiters, consider using {@link #delimitedListToStringArray}.
+ * @param str the {@code String} to tokenize
+ * @param delimiters the delimiter characters, assembled as a {@code String}
+ * (each of the characters is individually considered as a delimiter)
+ * @param trimTokens trim the tokens via {@link String#trim()}
+ * @param ignoreEmptyTokens omit empty tokens from the result array
+ * (only applies to tokens that are empty after trimming; StringTokenizer
+ * will not consider subsequent delimiters as token in the first place).
+ * @return an array of the tokens
+ * @see StringTokenizer
+ * @see String#trim()
+ * @see #delimitedListToStringArray
+ */
+ public static String[] tokenizeToStringArray(
+ String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
+
+ if (str == null) {
+ return null;
+ }
+
+ StringTokenizer st = new StringTokenizer(str, delimiters);
+ List tokens = new ArrayList();
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (trimTokens) {
+ token = token.trim();
+ }
+ if (!ignoreEmptyTokens || token.length() > 0) {
+ tokens.add(token);
+ }
+ }
+ return toStringArray(tokens);
+ }
+
+ /**
+ * Take a {@code String} that is a delimited list and convert it into a
+ * {@code String} array.
+ * A single {@code delimiter} may consist of more than one character,
+ * but it will still be considered as a single delimiter string, rather
+ * than as bunch of potential delimiter characters, in contrast to
+ * {@link #tokenizeToStringArray}.
+ * @param str the input {@code String}
+ * @param delimiter the delimiter between elements (this is a single delimiter,
+ * rather than a bunch individual delimiter characters)
+ * @return an array of the tokens in the list
+ * @see #tokenizeToStringArray
+ */
+ public static String[] delimitedListToStringArray(String str, String delimiter) {
+ return delimitedListToStringArray(str, delimiter, null);
+ }
+
+ /**
+ * Take a {@code String} that is a delimited list and convert it into
+ * a {@code String} array.
+ *
A single {@code delimiter} may consist of more than one character,
+ * but it will still be considered as a single delimiter string, rather
+ * than as bunch of potential delimiter characters, in contrast to
+ * {@link #tokenizeToStringArray}.
+ * @param str the input {@code String}
+ * @param delimiter the delimiter between elements (this is a single delimiter,
+ * rather than a bunch individual delimiter characters)
+ * @param charsToDelete a set of characters to delete; useful for deleting unwanted
+ * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a {@code String}
+ * @return an array of the tokens in the list
+ * @see #tokenizeToStringArray
+ */
+ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
+ if (str == null) {
+ return new String[0];
+ }
+ if (delimiter == null) {
+ return new String[] {str};
+ }
+
+ List result = new ArrayList();
+ if ("".equals(delimiter)) {
+ for (int i = 0; i < str.length(); i++) {
+ result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
+ }
+ }
+ else {
+ int pos = 0;
+ int delPos;
+ while ((delPos = str.indexOf(delimiter, pos)) != -1) {
+ result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
+ pos = delPos + delimiter.length();
+ }
+ if (str.length() > 0 && pos <= str.length()) {
+ // Add rest of String, but not in case of empty input.
+ result.add(deleteAny(str.substring(pos), charsToDelete));
+ }
+ }
+ return toStringArray(result);
+ }
+
+ /**
+ * Convert a comma delimited list (e.g., a row from a CSV file) into an
+ * array of strings.
+ * @param str the input {@code String}
+ * @return an array of strings, or the empty array in case of empty input
+ */
+ public static String[] commaDelimitedListToStringArray(String str) {
+ return delimitedListToStringArray(str, ",");
+ }
+
+ /**
+ * Convert a comma delimited list (e.g., a row from a CSV file) into a set.
+ * Note that this will suppress duplicates, and as of 4.2, the elements in
+ * the returned set will preserve the original order in a {@link LinkedHashSet}.
+ * @param str the input {@code String}
+ * @return a set of {@code String} entries in the list
+ * @see #removeDuplicateStrings(String[])
+ */
+ public static Set commaDelimitedListToSet(String str) {
+ Set set = new LinkedHashSet();
+ String[] tokens = commaDelimitedListToStringArray(str);
+ for (String token : tokens) {
+ set.add(token);
+ }
+ return set;
+ }
+
+ /**
+ * Convert a {@link Collection} to a delimited {@code String} (e.g. CSV).
+ * Useful for {@code toString()} implementations.
+ * @param coll the {@code Collection} to convert
+ * @param delim the delimiter to use (typically a ",")
+ * @param prefix the {@code String} to start each element with
+ * @param suffix the {@code String} to end each element with
+ * @return the delimited {@code String}
+ */
+ public static String collectionToDelimitedString(Collection> coll, String delim, String prefix, String suffix) {
+ if (CollectionUtils.isEmpty(coll)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ Iterator> it = coll.iterator();
+ while (it.hasNext()) {
+ sb.append(prefix).append(it.next()).append(suffix);
+ if (it.hasNext()) {
+ sb.append(delim);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert a {@code Collection} into a delimited {@code String} (e.g. CSV).
+ *
Useful for {@code toString()} implementations.
+ * @param coll the {@code Collection} to convert
+ * @param delim the delimiter to use (typically a ",")
+ * @return the delimited {@code String}
+ */
+ public static String collectionToDelimitedString(Collection> coll, String delim) {
+ return collectionToDelimitedString(coll, delim, "", "");
+ }
+
+ /**
+ * Convert a {@code Collection} into a delimited {@code String} (e.g., CSV).
+ *
Useful for {@code toString()} implementations.
+ * @param coll the {@code Collection} to convert
+ * @return the delimited {@code String}
+ */
+ public static String collectionToCommaDelimitedString(Collection> coll) {
+ return collectionToDelimitedString(coll, ",");
+ }
+
+ /**
+ * Convert a {@code String} array into a delimited {@code String} (e.g. CSV).
+ *
Useful for {@code toString()} implementations.
+ * @param arr the array to display
+ * @param delim the delimiter to use (typically a ",")
+ * @return the delimited {@code String}
+ */
+ public static String arrayToDelimitedString(Object[] arr, String delim) {
+ if (ObjectUtils.isEmpty(arr)) {
+ return "";
+ }
+ if (arr.length == 1) {
+ return ObjectUtils.nullSafeToString(arr[0]);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < arr.length; i++) {
+ if (i > 0) {
+ sb.append(delim);
+ }
+ sb.append(arr[i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert a {@code String} array into a comma delimited {@code String}
+ * (i.e., CSV).
+ *
Useful for {@code toString()} implementations.
+ * @param arr the array to display
+ * @return the delimited {@code String}
+ */
+ public static String arrayToCommaDelimitedString(Object[] arr) {
+ return arrayToDelimitedString(arr, ",");
+ }
+
+}
diff --git a/src/main/java/com/alibaba/excel/util/StyleUtil.java b/src/main/java/com/alibaba/excel/util/StyleUtil.java
new file mode 100644
index 00000000..6fa311c2
--- /dev/null
+++ b/src/main/java/com/alibaba/excel/util/StyleUtil.java
@@ -0,0 +1,68 @@
+package com.alibaba.excel.util;
+
+import org.apache.poi.ss.usermodel.*;
+
+import java.util.Map;
+
+/**
+ * @author jipengfei
+ * @date 2017/03/15
+ */
+public class StyleUtil {
+
+ /**
+ *
+ * @param workbook
+ * @return
+ */
+ public static CellStyle buildDefaultCellStyle(Workbook workbook) {
+ CellStyle newCellStyle = workbook.createCellStyle();
+ Font font = workbook.createFont();
+ font.setFontName("宋体");
+ font.setFontHeightInPoints((short)14);
+ font.setBold(true);
+ newCellStyle.setFont(font);
+ newCellStyle.setWrapText(true);
+ newCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ newCellStyle.setAlignment(HorizontalAlignment.CENTER);
+ newCellStyle.setLocked(true);
+ newCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ newCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ newCellStyle.setBorderBottom(BorderStyle.THIN);
+ newCellStyle.setBorderLeft(BorderStyle.THIN);
+ return newCellStyle;
+ }
+
+ /**
+ *
+ * @param workbook
+ * @param f
+ * @param indexedColors
+ * @return
+ */
+ public static CellStyle buildCellStyle(Workbook workbook, com.alibaba.excel.metadata.Font f,
+ IndexedColors indexedColors) {
+ CellStyle cellStyle = buildDefaultCellStyle(workbook);
+ if (f != null) {
+ Font font = workbook.createFont();
+ font.setFontName(f.getFontName());
+ font.setFontHeightInPoints(f.getFontHeightInPoints());
+ font.setBold(f.isBold());
+ cellStyle.setFont(font);
+ }
+ if (indexedColors != null) {
+ cellStyle.setFillForegroundColor(indexedColors.getIndex());
+ }
+ return cellStyle;
+ }
+
+ public static Sheet buildSheetStyle(Sheet currentSheet, Map sheetWidthMap){
+ currentSheet.setDefaultColumnWidth(20);
+ for (Map.Entry entry : sheetWidthMap.entrySet()) {
+ currentSheet.setColumnWidth(entry.getKey(), entry.getValue());
+ }
+ return currentSheet;
+ }
+
+
+}
diff --git a/src/main/java/com/alibaba/excel/util/TypeUtil.java b/src/main/java/com/alibaba/excel/util/TypeUtil.java
index 564bf2e5..a04e874d 100644
--- a/src/main/java/com/alibaba/excel/util/TypeUtil.java
+++ b/src/main/java/com/alibaba/excel/util/TypeUtil.java
@@ -1,27 +1,30 @@
package com.alibaba.excel.util;
+import com.alibaba.excel.metadata.ExcelColumnProperty;
+import com.alibaba.excel.metadata.ExcelHeadProperty;
+import net.sf.cglib.beans.BeanMap;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author jipengfei
+ * @date 2017/03/15
*/
public class TypeUtil {
- private static List DATE_FORMAT_LIST = new ArrayList(4);
+ private static List DATE_FORMAT_LIST = new ArrayList(4);
static {
- DATE_FORMAT_LIST.add(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"));
- DATE_FORMAT_LIST.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+ DATE_FORMAT_LIST.add("yyyy/MM/dd HH:mm:ss");
+ DATE_FORMAT_LIST.add("yyyy-MM-dd HH:mm:ss");
+ DATE_FORMAT_LIST.add("yyyyMMdd HH:mm:ss");
}
private static int getCountOfChar(String value, char c) {
@@ -39,9 +42,9 @@ public class TypeUtil {
}
public static Object convert(String value, Field field, String format, boolean us) {
- if (isNotEmpty(value)) {
- if (String.class.equals(field.getType())) {
- return TypeUtil.formatFloat(value);
+ if (!StringUtils.isEmpty(value)) {
+ if (Float.class.equals(field.getType())) {
+ return Float.parseFloat(value);
}
if (Integer.class.equals(field.getType()) || int.class.equals(field.getType())) {
return Integer.parseInt(value);
@@ -80,6 +83,9 @@ public class TypeUtil {
if (BigDecimal.class.equals(field.getType())) {
return new BigDecimal(value);
}
+ if(String.class.equals(field.getType())){
+ return formatFloat(value);
+ }
}
return null;
@@ -106,6 +112,18 @@ public class TypeUtil {
return false;
}
+ public static Boolean isNum(Object cellValue) {
+ if (cellValue instanceof Integer
+ || cellValue instanceof Double
+ || cellValue instanceof Short
+ || cellValue instanceof Long
+ || cellValue instanceof Float
+ || cellValue instanceof BigDecimal) {
+ return true;
+ }
+ return false;
+ }
+
public static String getDefaultDateString(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(date);
@@ -113,9 +131,9 @@ public class TypeUtil {
}
public static Date getSimpleDateFormatDate(String value, String format) {
- if (isNotEmpty(value)) {
+ if (!StringUtils.isEmpty(value)) {
Date date = null;
- if (isNotEmpty(format)) {
+ if (!StringUtils.isEmpty(format)) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
date = simpleDateFormat.parse(value);
@@ -123,9 +141,10 @@ public class TypeUtil {
} catch (ParseException e) {
}
}
- for (SimpleDateFormat dateFormat : DATE_FORMAT_LIST) {
+ for (String dateFormat : DATE_FORMAT_LIST) {
try {
- date = dateFormat.parse(value);
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
+ date = simpleDateFormat.parse(value);
} catch (ParseException e) {
}
if (date != null) {
@@ -140,16 +159,6 @@ public class TypeUtil {
}
- public static Boolean isNotEmpty(String value) {
- if (value == null) {
- return false;
- }
- if (value.trim().equals("")) {
- return false;
- }
- return true;
-
- }
public static String formatFloat(String value) {
if (null != value && value.contains(".")) {
@@ -190,12 +199,40 @@ public class TypeUtil {
}
public static String formatDate(Date cellValue, String format) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
-
+ SimpleDateFormat simpleDateFormat;
+ if(!StringUtils.isEmpty(format)) {
+ simpleDateFormat = new SimpleDateFormat(format);
+ }else {
+ simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ }
return simpleDateFormat.format(cellValue);
}
- public static void main(String[] args) {
- System.out.println(new Date().toString());
+ public static String getFieldStringValue(BeanMap beanMap, String fieldName, String format) {
+ String cellValue = null;
+ Object value = beanMap.get(fieldName);
+ if (value != null) {
+ if (value instanceof Date) {
+ cellValue = TypeUtil.formatDate((Date)value, format);
+ } else {
+ cellValue = value.toString();
+ }
+ }
+ return cellValue;
+ }
+
+ public static Map getFieldValues(List stringList, ExcelHeadProperty excelHeadProperty, Boolean use1904WindowDate) {
+ Map map = new HashMap();
+ for (int i = 0; i < stringList.size(); i++) {
+ ExcelColumnProperty columnProperty = excelHeadProperty.getExcelColumnProperty(i);
+ if (columnProperty != null) {
+ Object value = TypeUtil.convert(stringList.get(i), columnProperty.getField(),
+ columnProperty.getFormat(), use1904WindowDate);
+ if (value != null) {
+ map.put(columnProperty.getField().getName(),value);
+ }
+ }
+ }
+ return map;
}
}
diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java
new file mode 100644
index 00000000..55369292
--- /dev/null
+++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java
@@ -0,0 +1,75 @@
+package com.alibaba.excel.util;
+
+import com.alibaba.excel.support.ExcelTypeEnum;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.alibaba.excel.util.StyleUtil.buildSheetStyle;
+
+/**
+ *
+ * @author jipengfei
+ */
+public class WorkBookUtil {
+
+ public static Workbook createWorkBook(InputStream templateInputStream, ExcelTypeEnum excelType) throws IOException {
+ Workbook workbook;
+ if (ExcelTypeEnum.XLS.equals(excelType)) {
+ workbook = (templateInputStream == null) ? new HSSFWorkbook() : new HSSFWorkbook(
+ new POIFSFileSystem(templateInputStream));
+ } else {
+ workbook = (templateInputStream == null) ? new SXSSFWorkbook(500) : new SXSSFWorkbook(
+ new XSSFWorkbook(templateInputStream));
+ }
+ return workbook;
+ }
+
+ public static Sheet createOrGetSheet(Workbook workbook, com.alibaba.excel.metadata.Sheet sheet) {
+ Sheet sheet1 = null;
+ try {
+ try {
+ sheet1 = workbook.getSheetAt(sheet.getSheetNo()-1);
+ } catch (Exception e) {
+ }
+ if (null == sheet1) {
+ sheet1 = createSheet(workbook, sheet);
+ buildSheetStyle(sheet1,sheet.getColumnWidthMap());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("constructCurrentSheet error", e);
+ }
+ return sheet1;
+ }
+
+ public static Sheet createSheet(Workbook workbook, com.alibaba.excel.metadata.Sheet sheet) {
+ return workbook.createSheet(sheet.getSheetName() != null ? sheet.getSheetName() : sheet.getSheetNo() + "");
+ }
+
+ public static Row createRow(Sheet sheet, int rowNum) {
+ return sheet.createRow(rowNum);
+ }
+
+ public static Cell createCell(Row row, int colNum, CellStyle cellStyle, String cellValue) {
+ return createCell(row, colNum, cellStyle, cellValue, false);
+ }
+
+ public static Cell createCell(Row row, int colNum, CellStyle cellStyle, Object cellValue, Boolean isNum) {
+ Cell cell = row.createCell(colNum);
+ cell.setCellStyle(cellStyle);
+ if (null != cellValue) {
+ if (isNum) {
+ cell.setCellValue(Double.parseDouble(cellValue.toString()));
+ } else {
+ cell.setCellValue(cellValue.toString());
+ }
+ }
+ return cell;
+ }
+
+}
diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java
index 75acb585..787807ef 100644
--- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java
+++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java
@@ -2,30 +2,52 @@ package com.alibaba.excel.write;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
-import com.alibaba.excel.support.ExcelTypeEnum;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.List;
/**
- *
* @author jipengfei
*/
public interface ExcelBuilder {
- void init(InputStream templateInputStream, OutputStream out, ExcelTypeEnum excelType, boolean needHead);
-
-
+ /**
+ * workBook increase data
+ *
+ * @param data List> or List extends BaseRowModel>
+ * @param startRow Start row number
+ */
void addContent(List data, int startRow);
-
+ /**
+ * WorkBook increase data
+ *
+ * @param data List> or List extends BaseRowModel>
+ * @param sheetParam Write the sheet
+ */
void addContent(List data, Sheet sheetParam);
-
+ /**
+ * WorkBook increase data
+ *
+ * @param data List> or List extends BaseRowModel>
+ * @param sheetParam Write the sheet
+ * @param table Write the table
+ */
void addContent(List data, Sheet sheetParam, Table table);
-
+ /**
+ * Creates new cell range. Indexes are zero-based.
+ *
+ * @param firstRow Index of first row
+ * @param lastRow Index of last row (inclusive), must be equal to or larger than {@code firstRow}
+ * @param firstCol Index of first column
+ * @param lastCol Index of last column (inclusive), must be equal to or larger than {@code firstCol}
+ */
+ void merge(int firstRow, int lastRow, int firstCol, int lastCol);
+
+ /**
+ * Close io
+ */
void finish();
}
diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
index b81b73d8..fd993aa0 100644
--- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
+++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
@@ -1,38 +1,42 @@
package com.alibaba.excel.write;
-import com.alibaba.excel.context.GenerateContext;
-import com.alibaba.excel.context.GenerateContextImpl;
+import com.alibaba.excel.context.WriteContext;
+import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.ExcelColumnProperty;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.POITempFile;
import com.alibaba.excel.util.TypeUtil;
-import org.apache.commons.beanutils.BeanUtilsBean;
-import org.apache.poi.ss.usermodel.Cell;
+import com.alibaba.excel.util.WorkBookUtil;
+import net.sf.cglib.beans.BeanMap;
+import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.util.CellRangeAddress;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Date;
import java.util.List;
/**
* @author jipengfei
+ * @date 2017/05/27
*/
public class ExcelBuilderImpl implements ExcelBuilder {
- private GenerateContext context;
+ private WriteContext context;
- @Override
- public void init(InputStream templateInputStream, OutputStream out, ExcelTypeEnum excelType, boolean needHead) {
+ public ExcelBuilderImpl(InputStream templateInputStream,
+ OutputStream out,
+ ExcelTypeEnum excelType,
+ boolean needHead) {
try {
//初始化时候创建临时缓存目录,用于规避POI在并发写bug
POITempFile.createPOIFilesDirectory();
-
- context = new GenerateContextImpl(templateInputStream, out, excelType, needHead);
+ context = new WriteContext(templateInputStream, out, excelType, needHead);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -40,115 +44,88 @@ public class ExcelBuilderImpl implements ExcelBuilder {
@Override
public void addContent(List data, int startRow) {
- if (data != null && data.size() > 0) {
- int rowNum = context.getCurrentSheet().getLastRowNum();
- if (rowNum == 0) {
- Row row = context.getCurrentSheet().getRow(0);
- if (row == null) {
- if (context.getExcelHeadProperty() == null || !context.needHead()) {
- rowNum = -1;
- }
+ if (CollectionUtils.isEmpty(data)) {
+ return;
+ }
+ int rowNum = context.getCurrentSheet().getLastRowNum();
+ if (rowNum == 0) {
+ Row row = context.getCurrentSheet().getRow(0);
+ if (row == null) {
+ if (context.getExcelHeadProperty() == null || !context.needHead()) {
+ rowNum = -1;
}
}
- if (rowNum < startRow) {
- rowNum = startRow;
- }
- for (int i = 0; i < data.size(); i++) {
- int n = i + rowNum + 1;
-
- addOneRowOfDataToExcel(data.get(i), n);
- }
+ }
+ if (rowNum < startRow) {
+ rowNum = startRow;
+ }
+ for (int i = 0; i < data.size(); i++) {
+ int n = i + rowNum + 1;
+ addOneRowOfDataToExcel(data.get(i), n);
}
}
@Override
public void addContent(List data, Sheet sheetParam) {
- context.buildCurrentSheet(sheetParam);
+ context.currentSheet(sheetParam);
addContent(data, sheetParam.getStartRow());
}
@Override
public void addContent(List data, Sheet sheetParam, Table table) {
- context.buildCurrentSheet(sheetParam);
- context.buildTable(table);
+ context.currentSheet(sheetParam);
+ context.currentTable(table);
addContent(data, sheetParam.getStartRow());
}
+ @Override
+ public void merge(int firstRow, int lastRow, int firstCol, int lastCol) {
+ CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+ context.getCurrentSheet().addMergedRegion(cra);
+ }
+
@Override
public void finish() {
try {
context.getWorkbook().write(context.getOutputStream());
+ context.getWorkbook().close();
} catch (IOException e) {
- e.printStackTrace();
+ throw new ExcelGenerateException("IO error", e);
}
}
- private void addOneRowOfDataToExcel(List