Browse Source

完成csv转换书写

developing
Jiaju Zhuang 3 years ago
parent
commit
7a2c5634d0
  1. 22
      src/main/java/com/alibaba/excel/enums/NumericCellTypeEnum.java
  2. 40
      src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java
  3. 8
      src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java
  4. 45
      src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java
  5. 20
      src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java
  6. 4
      src/main/java/com/alibaba/excel/metadata/data/DataFormatData.java
  7. 112
      src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java
  8. 25
      src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java
  9. 4
      src/main/java/com/alibaba/excel/util/WorkBookUtil.java

22
src/main/java/com/alibaba/excel/enums/NumericCellTypeEnum.java

@ -0,0 +1,22 @@
package com.alibaba.excel.enums;
import org.apache.poi.ss.usermodel.CellType;
/**
* Used to supplement {@link CellType}.
*
* Cannot distinguish between date and number in write case.
*
* @author Jiaju Zhuang
*/
public enum NumericCellTypeEnum {
/**
* number
*/
NUMBER,
/**
* date. Support only when writing.
*/
DATE,
;
}

40
src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java

@ -18,7 +18,6 @@ import org.apache.poi.ss.usermodel.CellBase;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
@ -47,6 +46,11 @@ public class CsvCell extends CellBase {
@Setter(value = AccessLevel.NONE)
private CellType cellType;
/**
* numeric cell type
*/
private NumericCellTypeEnum numericCellType;
/**
* workbook
*/
@ -65,7 +69,7 @@ public class CsvCell extends CellBase {
/**
* {@link CellType#NUMERIC}
*/
private Double numberValue;
private BigDecimal numberValue;
/**
* {@link CellType#STRING} and {@link CellType#ERROR} {@link CellType#FORMULA}
*/
@ -75,6 +79,11 @@ public class CsvCell extends CellBase {
*/
private Boolean booleanValue;
/**
* {@link CellType#NUMERIC}
*/
private LocalDateTime dateValue;
/**
* formula
*/
@ -121,25 +130,33 @@ public class CsvCell extends CellBase {
@Override
protected void setCellValueImpl(double value) {
this.numberValue = value;
numberValue = BigDecimal.valueOf(value);
this.cellType = CellType.NUMERIC;
}
@Override
protected void setCellValueImpl(Date value) {
this.numberValue = DateUtil.getExcelDate(value, csvWorkbook.getUse1904windowing());
if (value == null) {
return;
}
this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault());
this.cellType = CellType.NUMERIC;
this.numericCellType = NumericCellTypeEnum.DATE;
}
@Override
protected void setCellValueImpl(LocalDateTime value) {
this.numberValue = DateUtil.getExcelDate(value, csvWorkbook.getUse1904windowing());
this.dateValue = value;
this.cellType = CellType.NUMERIC;
this.numericCellType = NumericCellTypeEnum.DATE;
}
@Override
protected void setCellValueImpl(Calendar value) {
this.numberValue = DateUtil.getExcelDate(value, csvWorkbook.getUse1904windowing());
if (value == null) {
return;
}
this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault());
this.cellType = CellType.NUMERIC;
}
@ -231,18 +248,15 @@ public class CsvCell extends CellBase {
@Override
public Date getDateCellValue() {
if (numberValue == null) {
if (dateValue == null) {
return null;
}
return DateUtil.getJavaDate(numberValue, csvWorkbook.getUse1904windowing());
return Date.from(dateValue.atZone(ZoneId.systemDefault()).toInstant());
}
@Override
public LocalDateTime getLocalDateTimeCellValue() {
if (numberValue == null) {
return null;
}
return DateUtil.getLocalDateTime(numberValue, csvWorkbook.getUse1904windowing());
return dateValue;
}
@Override
@ -263,7 +277,7 @@ public class CsvCell extends CellBase {
@Override
public void setCellErrorValue(byte value) {
this.numberValue = value;
this.numberValue = BigDecimal.valueOf(value);
this.cellType = CellType.ERROR;
}

8
src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java

@ -52,13 +52,17 @@ public class CsvCellStyle implements CellStyle {
@Override
public short getDataFormat() {
initDataFormatData();
if (dataFormatData == null) {
return 0;
}
return dataFormatData.getIndex();
}
@Override
public String getDataFormatString() {
initDataFormatData();
if (dataFormatData == null) {
return null;
}
return dataFormatData.getFormat();
}

45
src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java

@ -7,9 +7,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.alibaba.excel.constant.BuiltinFormats;
import com.alibaba.excel.enums.NumericCellTypeEnum;
import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.NumberDataFormatterUtils;
@ -25,6 +25,7 @@ import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.Header;
@ -45,7 +46,6 @@ import org.apache.poi.ss.util.PaneInformation;
*/
@Data
public class CsvSheet implements Sheet, Closeable {
/**
* workbook
*/
@ -747,21 +747,40 @@ public class CsvSheet implements Sheet, Closeable {
case FORMULA:
return csvCell.getStringCellValue();
case NUMERIC:
NumberDataFormatterUtils.format()
Short dataFormat = null;
String dataFormatString = null;
if (csvCell.getCellStyle() != null) {
dataFormat = csvCell.getCellStyle().getDataFormat();
dataFormatString = csvCell.getCellStyle().getDataFormatString();
}
if (csvCell.getNumericCellType() == NumericCellTypeEnum.DATE) {
if (csvCell.getDateValue() == null) {
return null;
}
// date
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return new WriteCellData<>(DateUtils.format(value, null));
} else {
return new WriteCellData<>(
DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat()));
if (dataFormat == null) {
dataFormatString = DateUtils.defaultDateFormat;
dataFormat = csvWorkbook.createDataFormat().getFormat(dataFormatString);
}
if (dataFormatString == null) {
dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat);
}
return NumberDataFormatterUtils.format(
DateUtil.getExcelDate(csvCell.getDateValue(), csvWorkbook.getUse1904windowing()),
dataFormat, dataFormatString, csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(),
csvWorkbook.getUseScientificFormat());
} else {
if (csvCell.getNumberValue() == null) {
return null;
}
//number
if (dataFormat == null) {
dataFormat = BuiltinFormats.GENERAL;
dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat);
}
return NumberDataFormatterUtils.format(csvCell.getNumericCellValue(), dataFormat, dataFormatString,
csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(),
csvWorkbook.getUseScientificFormat());
}
case BOOLEAN:
return csvCell.getBooleanValue().toString();
@ -772,6 +791,4 @@ public class CsvSheet implements Sheet, Closeable {
}
}
private String
}

20
src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java

@ -32,10 +32,7 @@ public class CsvWorkbook implements Workbook {
* output
*/
private Appendable out;
/**
* locale
*/
private Locale locale;
/**
* true if date uses 1904 windowing, or false if using 1900 date windowing.
*
@ -45,6 +42,18 @@ public class CsvWorkbook implements Workbook {
*/
private Boolean use1904windowing;
/**
* locale
*/
private Locale locale;
/**
* Whether to use scientific Format.
*
* default is false
*/
private Boolean useScientificFormat;
/**
* data format
*/
@ -58,10 +67,11 @@ public class CsvWorkbook implements Workbook {
*/
private List<CsvCellStyle> csvCellStyleList;
public CsvWorkbook(Appendable out, Locale locale, Boolean use1904windowing) {
public CsvWorkbook(Appendable out, Locale locale, Boolean use1904windowing, Boolean useScientificFormat) {
this.out = out;
this.locale = locale;
this.use1904windowing = use1904windowing;
this.useScientificFormat = useScientificFormat;
}
@Override

4
src/main/java/com/alibaba/excel/metadata/data/DataFormatData.java

@ -19,10 +19,6 @@ public class DataFormatData {
*/
private String format;
public DataFormatData() {
this.index = 0;
}
@Override
public DataFormatData clone() {
DataFormatData dataFormatData = new DataFormatData();

112
src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java

@ -34,6 +34,8 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.alibaba.excel.util.DateUtils;
import org.apache.poi.ss.format.CellFormat;
import org.apache.poi.ss.format.CellFormatResult;
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.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
* conversion.
@ -54,21 +53,31 @@ import com.alibaba.excel.util.DateUtils;
* @author Jiaju Zhuang
*/
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 String defaultFractionWholePartFormat = "#";
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#]+");
/** 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);
/** Pattern to find "AM/PM" marker */
/**
* Pattern to find "AM/PM" marker
*/
private static final Pattern amPmPattern =
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 =
Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*");
@ -107,10 +116,10 @@ public class DataFormatter {
* ("#").
*/
private static final String invalidDateTimeString;
static {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < 255; i++)
buf.append('#');
for (int i = 0; i < 255; i++) {buf.append('#');}
invalidDateTimeString = buf.toString();
}
@ -123,14 +132,18 @@ public class DataFormatter {
* The date symbols of the locale used for formatting values.
*/
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;
/**
* A map to cache formats. Map<String,Format> formats
*/
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;
/**
* 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.
*
*/
public DataFormatter(GlobalConfiguration globalConfiguration) {
if (globalConfiguration == null) {
public DataFormatter(Boolean use1904windowing, Locale locale, Boolean useScientificFormat) {
if (use1904windowing == null) {
this.use1904windowing = Boolean.FALSE;
} else {
this.use1904windowing = use1904windowing;
}
if (locale == null) {
this.locale = Locale.getDefault();
} else {
this.locale = locale;
}
if (use1904windowing == null) {
this.useScientificFormat = Boolean.FALSE;
} else {
this.use1904windowing =
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.useScientificFormat = useScientificFormat;
}
this.dateSymbols = DateFormatSymbols.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.
// That however would require other code to be re factored.
@ -188,7 +204,7 @@ public class DataFormatter {
if (formatStr.contains(";") &&
(formatStr.indexOf(';') != formatStr.lastIndexOf(';')
|| rangeConditionalPattern.matcher(formatStr).matches()
) ) {
)) {
try {
// Ask CellFormat to get a formatter for it
CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
@ -200,9 +216,9 @@ public class DataFormatter {
cellValueO = DateUtil.getJavaDate(data, use1904windowing);
}
// Wrap and return (non-cachable - CellFormat does that)
return new CellFormatResultWrapper( cfmt.apply(cellValueO) );
return new CellFormatResultWrapper(cfmt.apply(cellValueO));
} 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...
int at = formatStr.indexOf(colour);
if (at == -1)
break;
if (at == -1) {break;}
String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length());
if (nFormatStr.equals(formatStr))
break;
if (nFormatStr.equals(formatStr)) {break;}
// Try again in case there's multiple
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
* default date format is applied.
*
* @param data
* to format
* @param data to format
* @param dataFormat
* @param dataFormatString
* @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
* style.
*
* @param data
* to format
* @param data to format
* @param dataFormat
* @param dataFormatString
* @return a formatted number string
@ -663,8 +675,7 @@ public class DataFormatter {
* <code>Number</code> value.
* </p>
*
* @param format
* A Format instance to be used as a default
* @param format A Format instance to be used as a default
* @see Format#format
*/
public void setDefaultNumberFormat(Format format) {
@ -684,10 +695,8 @@ public class DataFormatter {
* <code>Number</code> value.
* </p>
*
* @param excelFormatStr
* The data format string
* @param format
* A Format instance
* @param excelFormatStr The data format string
* @param format A Format instance
*/
public void addFormat(String excelFormatStr, Format format) {
formats.put(excelFormatStr, format);
@ -715,10 +724,8 @@ public class DataFormatter {
/**
* Enables custom rounding mode on the given Decimal Format.
*
* @param format
* DecimalFormat
* @param roundingMode
* RoundingMode
* @param format DecimalFormat
* @param roundingMode RoundingMode
*/
public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
format.setRoundingMode(roundingMode);
@ -737,7 +744,9 @@ public class DataFormatter {
// enforce singleton
}
/** Format a number as an SSN */
/**
* Format a number as an SSN
*/
public static String format(Number num) {
String result = df.format(num);
return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9);
@ -767,7 +776,9 @@ public class DataFormatter {
// enforce singleton
}
/** Format a number as Zip + 4 */
/**
* Format a number as Zip + 4
*/
public static String format(Number num) {
String result = df.format(num);
return result.substring(0, 5) + '-' + result.substring(5, 9);
@ -797,7 +808,9 @@ public class DataFormatter {
// enforce singleton
}
/** Format a number as a phone number */
/**
* Format a number as a phone number
*/
public static String format(Number num) {
String result = df.format(num);
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}
*/
private final class CellFormatResultWrapper extends Format {

25
src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java

@ -1,5 +1,7 @@
package com.alibaba.excel.util;
import java.util.Locale;
import com.alibaba.excel.metadata.format.DataFormatter;
import com.alibaba.excel.metadata.GlobalConfiguration;
@ -26,13 +28,32 @@ public class NumberDataFormatterUtils {
*/
public static String format(Double data, Short dataFormat, String dataFormatString,
GlobalConfiguration globalConfiguration) {
if (globalConfiguration == null) {
return format(data, dataFormat, dataFormatString, null, null, null);
}
return format(data, dataFormat, dataFormatString, globalConfiguration.getUse1904windowing(),
globalConfiguration.getLocale(), globalConfiguration.getUseScientificFormat());
}
/**
* Format number data.
*
* @param data
* @param dataFormat Not null.
* @param dataFormatString
* @param use1904windowing
* @param locale
* @param useScientificFormat
* @return
*/
public static String format(Double data, Short dataFormat, String dataFormatString, Boolean use1904windowing,
Locale locale, Boolean useScientificFormat) {
DataFormatter dataFormatter = DATA_FORMATTER_THREAD_LOCAL.get();
if (dataFormatter == null) {
dataFormatter = new DataFormatter(globalConfiguration);
dataFormatter = new DataFormatter(use1904windowing, locale, useScientificFormat);
DATA_FORMATTER_THREAD_LOCAL.set(dataFormatter);
}
return dataFormatter.format(data, dataFormat, dataFormatString);
}
public static void removeThreadLocalCache() {

4
src/main/java/com/alibaba/excel/util/WorkBookUtil.java

@ -69,7 +69,9 @@ public class WorkBookUtil {
case CSV:
CsvWorkbook csvWorkbook = new CsvWorkbook(
new OutputStreamWriter(writeWorkbookHolder.getOutputStream(), writeWorkbookHolder.getCharset()),
writeWorkbookHolder.getGlobalConfiguration().getLocale(), writeWorkbookHolder.getGlobalConfiguration().getUse1904windowing());
writeWorkbookHolder.getGlobalConfiguration().getLocale(),
writeWorkbookHolder.getGlobalConfiguration().getUse1904windowing(),
writeWorkbookHolder.getGlobalConfiguration().getUseScientificFormat());
writeWorkbookHolder.setCachedWorkbook(csvWorkbook);
writeWorkbookHolder.setWorkbook(csvWorkbook);
return;

Loading…
Cancel
Save