|
|
@ -34,6 +34,8 @@ import java.util.Map; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.alibaba.excel.util.DateUtils; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.poi.ss.format.CellFormat; |
|
|
|
import org.apache.poi.ss.format.CellFormat; |
|
|
|
import org.apache.poi.ss.format.CellFormatResult; |
|
|
|
import org.apache.poi.ss.format.CellFormatResult; |
|
|
|
import org.apache.poi.ss.usermodel.DateUtil; |
|
|
|
import org.apache.poi.ss.usermodel.DateUtil; |
|
|
@ -42,9 +44,6 @@ import org.apache.poi.ss.usermodel.FractionFormat; |
|
|
|
import org.slf4j.Logger; |
|
|
|
import org.slf4j.Logger; |
|
|
|
import org.slf4j.LoggerFactory; |
|
|
|
import org.slf4j.LoggerFactory; |
|
|
|
|
|
|
|
|
|
|
|
import com.alibaba.excel.metadata.GlobalConfiguration; |
|
|
|
|
|
|
|
import com.alibaba.excel.util.DateUtils; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date |
|
|
|
* Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date |
|
|
|
* conversion. |
|
|
|
* conversion. |
|
|
@ -54,21 +53,31 @@ import com.alibaba.excel.util.DateUtils; |
|
|
|
* @author Jiaju Zhuang |
|
|
|
* @author Jiaju Zhuang |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class DataFormatter { |
|
|
|
public class DataFormatter { |
|
|
|
/** For logging any problems we find */ |
|
|
|
/** |
|
|
|
|
|
|
|
* For logging any problems we find |
|
|
|
|
|
|
|
*/ |
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(DataFormatter.class); |
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(DataFormatter.class); |
|
|
|
private static final String defaultFractionWholePartFormat = "#"; |
|
|
|
private static final String defaultFractionWholePartFormat = "#"; |
|
|
|
private static final String defaultFractionFractionPartFormat = "#/##"; |
|
|
|
private static final String defaultFractionFractionPartFormat = "#/##"; |
|
|
|
/** Pattern to find a number format: "0" or "#" */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Pattern to find a number format: "0" or "#" |
|
|
|
|
|
|
|
*/ |
|
|
|
private static final Pattern numPattern = Pattern.compile("[0#]+"); |
|
|
|
private static final Pattern numPattern = Pattern.compile("[0#]+"); |
|
|
|
|
|
|
|
|
|
|
|
/** Pattern to find days of week as text "ddd...." */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Pattern to find days of week as text "ddd...." |
|
|
|
|
|
|
|
*/ |
|
|
|
private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE); |
|
|
|
private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE); |
|
|
|
|
|
|
|
|
|
|
|
/** Pattern to find "AM/PM" marker */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Pattern to find "AM/PM" marker |
|
|
|
|
|
|
|
*/ |
|
|
|
private static final Pattern amPmPattern = |
|
|
|
private static final Pattern amPmPattern = |
|
|
|
Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE); |
|
|
|
Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE); |
|
|
|
|
|
|
|
|
|
|
|
/** Pattern to find formats with condition ranges e.g. [>=100] */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Pattern to find formats with condition ranges e.g. [>=100] |
|
|
|
|
|
|
|
*/ |
|
|
|
private static final Pattern rangeConditionalPattern = |
|
|
|
private static final Pattern rangeConditionalPattern = |
|
|
|
Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); |
|
|
|
Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); |
|
|
|
|
|
|
|
|
|
|
@ -107,10 +116,10 @@ public class DataFormatter { |
|
|
|
* ("#"). |
|
|
|
* ("#"). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static final String invalidDateTimeString; |
|
|
|
private static final String invalidDateTimeString; |
|
|
|
|
|
|
|
|
|
|
|
static { |
|
|
|
static { |
|
|
|
StringBuilder buf = new StringBuilder(); |
|
|
|
StringBuilder buf = new StringBuilder(); |
|
|
|
for (int i = 0; i < 255; i++) |
|
|
|
for (int i = 0; i < 255; i++) {buf.append('#');} |
|
|
|
buf.append('#'); |
|
|
|
|
|
|
|
invalidDateTimeString = buf.toString(); |
|
|
|
invalidDateTimeString = buf.toString(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -123,14 +132,18 @@ public class DataFormatter { |
|
|
|
* The date symbols of the locale used for formatting values. |
|
|
|
* The date symbols of the locale used for formatting values. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private DateFormatSymbols dateSymbols; |
|
|
|
private DateFormatSymbols dateSymbols; |
|
|
|
/** A default format to use when a number pattern cannot be parsed. */ |
|
|
|
/** |
|
|
|
|
|
|
|
* A default format to use when a number pattern cannot be parsed. |
|
|
|
|
|
|
|
*/ |
|
|
|
private Format defaultNumFormat; |
|
|
|
private Format defaultNumFormat; |
|
|
|
/** |
|
|
|
/** |
|
|
|
* A map to cache formats. Map<String,Format> formats |
|
|
|
* A map to cache formats. Map<String,Format> formats |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private final Map<String, Format> formats = new HashMap<String, Format>(); |
|
|
|
private final Map<String, Format> formats = new HashMap<String, Format>(); |
|
|
|
|
|
|
|
|
|
|
|
/** stores the locale valid it the last formatting call */ |
|
|
|
/** |
|
|
|
|
|
|
|
* stores the locale valid it the last formatting call |
|
|
|
|
|
|
|
*/ |
|
|
|
private Locale locale; |
|
|
|
private Locale locale; |
|
|
|
/** |
|
|
|
/** |
|
|
|
* true if date uses 1904 windowing, or false if using 1900 date windowing. |
|
|
|
* true if date uses 1904 windowing, or false if using 1900 date windowing. |
|
|
@ -149,28 +162,31 @@ public class DataFormatter { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Creates a formatter using the given locale. |
|
|
|
* Creates a formatter using the given locale. |
|
|
|
* |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public DataFormatter(GlobalConfiguration globalConfiguration) { |
|
|
|
public DataFormatter(Boolean use1904windowing, Locale locale, Boolean useScientificFormat) { |
|
|
|
if (globalConfiguration == null) { |
|
|
|
if (use1904windowing == null) { |
|
|
|
this.use1904windowing = Boolean.FALSE; |
|
|
|
this.use1904windowing = Boolean.FALSE; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.use1904windowing = use1904windowing; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (locale == null) { |
|
|
|
this.locale = Locale.getDefault(); |
|
|
|
this.locale = Locale.getDefault(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.locale = locale; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (use1904windowing == null) { |
|
|
|
this.useScientificFormat = Boolean.FALSE; |
|
|
|
this.useScientificFormat = Boolean.FALSE; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.use1904windowing = |
|
|
|
this.useScientificFormat = useScientificFormat; |
|
|
|
globalConfiguration.getUse1904windowing() != null ? globalConfiguration.getUse1904windowing() |
|
|
|
|
|
|
|
: Boolean.FALSE; |
|
|
|
|
|
|
|
this.locale = |
|
|
|
|
|
|
|
globalConfiguration.getLocale() != null ? globalConfiguration.getLocale() : Locale.getDefault(); |
|
|
|
|
|
|
|
this.useScientificFormat = |
|
|
|
|
|
|
|
globalConfiguration.getUseScientificFormat() != null ? globalConfiguration.getUseScientificFormat() |
|
|
|
|
|
|
|
: Boolean.FALSE; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.dateSymbols = DateFormatSymbols.getInstance(this.locale); |
|
|
|
this.dateSymbols = DateFormatSymbols.getInstance(this.locale); |
|
|
|
this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale); |
|
|
|
this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Format getFormat(Double data,Short dataFormat, String dataFormatString) { |
|
|
|
private Format getFormat(Double data, Short dataFormat, String dataFormatString) { |
|
|
|
|
|
|
|
|
|
|
|
// Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
|
|
|
|
// Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
|
|
|
|
// That however would require other code to be re factored.
|
|
|
|
// That however would require other code to be re factored.
|
|
|
@ -188,7 +204,7 @@ public class DataFormatter { |
|
|
|
if (formatStr.contains(";") && |
|
|
|
if (formatStr.contains(";") && |
|
|
|
(formatStr.indexOf(';') != formatStr.lastIndexOf(';') |
|
|
|
(formatStr.indexOf(';') != formatStr.lastIndexOf(';') |
|
|
|
|| rangeConditionalPattern.matcher(formatStr).matches() |
|
|
|
|| rangeConditionalPattern.matcher(formatStr).matches() |
|
|
|
) ) { |
|
|
|
)) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
// Ask CellFormat to get a formatter for it
|
|
|
|
// Ask CellFormat to get a formatter for it
|
|
|
|
CellFormat cfmt = CellFormat.getInstance(locale, formatStr); |
|
|
|
CellFormat cfmt = CellFormat.getInstance(locale, formatStr); |
|
|
@ -200,9 +216,9 @@ public class DataFormatter { |
|
|
|
cellValueO = DateUtil.getJavaDate(data, use1904windowing); |
|
|
|
cellValueO = DateUtil.getJavaDate(data, use1904windowing); |
|
|
|
} |
|
|
|
} |
|
|
|
// Wrap and return (non-cachable - CellFormat does that)
|
|
|
|
// Wrap and return (non-cachable - CellFormat does that)
|
|
|
|
return new CellFormatResultWrapper( cfmt.apply(cellValueO) ); |
|
|
|
return new CellFormatResultWrapper(cfmt.apply(cellValueO)); |
|
|
|
} catch (Exception e) { |
|
|
|
} catch (Exception e) { |
|
|
|
LOGGER.warn("Formatting failed for format {}, falling back",formatStr, e); |
|
|
|
LOGGER.warn("Formatting failed for format {}, falling back", formatStr, e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -240,11 +256,9 @@ public class DataFormatter { |
|
|
|
|
|
|
|
|
|
|
|
// Paranoid replacement...
|
|
|
|
// Paranoid replacement...
|
|
|
|
int at = formatStr.indexOf(colour); |
|
|
|
int at = formatStr.indexOf(colour); |
|
|
|
if (at == -1) |
|
|
|
if (at == -1) {break;} |
|
|
|
break; |
|
|
|
|
|
|
|
String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); |
|
|
|
String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); |
|
|
|
if (nFormatStr.equals(formatStr)) |
|
|
|
if (nFormatStr.equals(formatStr)) {break;} |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Try again in case there's multiple
|
|
|
|
// Try again in case there's multiple
|
|
|
|
formatStr = nFormatStr; |
|
|
|
formatStr = nFormatStr; |
|
|
@ -601,8 +615,7 @@ public class DataFormatter { |
|
|
|
* number format, or no rules apply, the cell's style format is used. If the style does not have a format, the |
|
|
|
* number format, or no rules apply, the cell's style format is used. If the style does not have a format, the |
|
|
|
* default date format is applied. |
|
|
|
* default date format is applied. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param data |
|
|
|
* @param data to format |
|
|
|
* to format |
|
|
|
|
|
|
|
* @param dataFormat |
|
|
|
* @param dataFormat |
|
|
|
* @param dataFormatString |
|
|
|
* @param dataFormatString |
|
|
|
* @return Formatted value |
|
|
|
* @return Formatted value |
|
|
@ -624,8 +637,7 @@ public class DataFormatter { |
|
|
|
* Format comes from either the highest priority conditional format rule with a specified format, or from the cell |
|
|
|
* Format comes from either the highest priority conditional format rule with a specified format, or from the cell |
|
|
|
* style. |
|
|
|
* style. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param data |
|
|
|
* @param data to format |
|
|
|
* to format |
|
|
|
|
|
|
|
* @param dataFormat |
|
|
|
* @param dataFormat |
|
|
|
* @param dataFormatString |
|
|
|
* @param dataFormatString |
|
|
|
* @return a formatted number string |
|
|
|
* @return a formatted number string |
|
|
@ -663,8 +675,7 @@ public class DataFormatter { |
|
|
|
* <code>Number</code> value. |
|
|
|
* <code>Number</code> value. |
|
|
|
* </p> |
|
|
|
* </p> |
|
|
|
* |
|
|
|
* |
|
|
|
* @param format |
|
|
|
* @param format A Format instance to be used as a default |
|
|
|
* A Format instance to be used as a default |
|
|
|
|
|
|
|
* @see Format#format |
|
|
|
* @see Format#format |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setDefaultNumberFormat(Format format) { |
|
|
|
public void setDefaultNumberFormat(Format format) { |
|
|
@ -684,10 +695,8 @@ public class DataFormatter { |
|
|
|
* <code>Number</code> value. |
|
|
|
* <code>Number</code> value. |
|
|
|
* </p> |
|
|
|
* </p> |
|
|
|
* |
|
|
|
* |
|
|
|
* @param excelFormatStr |
|
|
|
* @param excelFormatStr The data format string |
|
|
|
* The data format string |
|
|
|
* @param format A Format instance |
|
|
|
* @param format |
|
|
|
|
|
|
|
* A Format instance |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void addFormat(String excelFormatStr, Format format) { |
|
|
|
public void addFormat(String excelFormatStr, Format format) { |
|
|
|
formats.put(excelFormatStr, format); |
|
|
|
formats.put(excelFormatStr, format); |
|
|
@ -715,10 +724,8 @@ public class DataFormatter { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Enables custom rounding mode on the given Decimal Format. |
|
|
|
* Enables custom rounding mode on the given Decimal Format. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param format |
|
|
|
* @param format DecimalFormat |
|
|
|
* DecimalFormat |
|
|
|
* @param roundingMode RoundingMode |
|
|
|
* @param roundingMode |
|
|
|
|
|
|
|
* RoundingMode |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { |
|
|
|
public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { |
|
|
|
format.setRoundingMode(roundingMode); |
|
|
|
format.setRoundingMode(roundingMode); |
|
|
@ -737,7 +744,9 @@ public class DataFormatter { |
|
|
|
// enforce singleton
|
|
|
|
// enforce singleton
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Format a number as an SSN */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Format a number as an SSN |
|
|
|
|
|
|
|
*/ |
|
|
|
public static String format(Number num) { |
|
|
|
public static String format(Number num) { |
|
|
|
String result = df.format(num); |
|
|
|
String result = df.format(num); |
|
|
|
return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9); |
|
|
|
return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9); |
|
|
@ -767,7 +776,9 @@ public class DataFormatter { |
|
|
|
// enforce singleton
|
|
|
|
// enforce singleton
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Format a number as Zip + 4 */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Format a number as Zip + 4 |
|
|
|
|
|
|
|
*/ |
|
|
|
public static String format(Number num) { |
|
|
|
public static String format(Number num) { |
|
|
|
String result = df.format(num); |
|
|
|
String result = df.format(num); |
|
|
|
return result.substring(0, 5) + '-' + result.substring(5, 9); |
|
|
|
return result.substring(0, 5) + '-' + result.substring(5, 9); |
|
|
@ -797,7 +808,9 @@ public class DataFormatter { |
|
|
|
// enforce singleton
|
|
|
|
// enforce singleton
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Format a number as a phone number */ |
|
|
|
/** |
|
|
|
|
|
|
|
* Format a number as a phone number |
|
|
|
|
|
|
|
*/ |
|
|
|
public static String format(Number num) { |
|
|
|
public static String format(Number num) { |
|
|
|
String result = df.format(num); |
|
|
|
String result = df.format(num); |
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
@ -833,7 +846,8 @@ public class DataFormatter { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Workaround until we merge {@link org.apache.poi.ss.usermodel.DataFormatter} with {@link CellFormat}. Constant, non-cachable wrapper around a |
|
|
|
* Workaround until we merge {@link org.apache.poi.ss.usermodel.DataFormatter} with {@link CellFormat}. Constant, |
|
|
|
|
|
|
|
* non-cachable wrapper around a |
|
|
|
* {@link CellFormatResult} |
|
|
|
* {@link CellFormatResult} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private final class CellFormatResultWrapper extends Format { |
|
|
|
private final class CellFormatResultWrapper extends Format { |
|
|
|