From d475f98b93133180004e8c347a1f3e495a6a4dd3 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Fri, 10 Sep 2021 14:31:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=83=A8=E5=88=86csv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/constant/BuiltinFormats.java | 24 +- .../java/com/alibaba/excel/csv/CsvCell.java | 231 -------------- .../com/alibaba/excel/csv/CsvDataFormat.java | 15 - .../alibaba/excel/metadata/csv/CsvCell.java | 291 ++++++++++++++++++ .../{ => metadata}/csv/CsvCellStyle.java | 21 +- .../excel/metadata/csv/CsvDataFormat.java | 65 ++++ .../excel/metadata/csv/CsvRichTextString.java | 68 ++++ .../excel/{ => metadata}/csv/CsvRow.java | 73 +++-- .../excel/{ => metadata}/csv/CsvSheet.java | 146 +++++++-- .../excel/{ => metadata}/csv/CsvWorkbook.java | 71 +++-- .../com/alibaba/excel/util/WorkBookUtil.java | 15 +- .../metadata/holder/WriteWorkbookHolder.java | 18 +- .../core/annotation/AnnotationDataTest.java | 16 +- 13 files changed, 718 insertions(+), 336 deletions(-) delete mode 100644 src/main/java/com/alibaba/excel/csv/CsvCell.java delete mode 100644 src/main/java/com/alibaba/excel/csv/CsvDataFormat.java create mode 100644 src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java rename src/main/java/com/alibaba/excel/{ => metadata}/csv/CsvCellStyle.java (93%) create mode 100644 src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java create mode 100644 src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java rename src/main/java/com/alibaba/excel/{ => metadata}/csv/CsvRow.java (59%) rename src/main/java/com/alibaba/excel/{ => metadata}/csv/CsvSheet.java (77%) rename src/main/java/com/alibaba/excel/{ => metadata}/csv/CsvWorkbook.java (80%) diff --git a/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java b/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java index b742cb6c..ec7d5c9a 100644 --- a/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java +++ b/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java @@ -1,6 +1,9 @@ package com.alibaba.excel.constant; import java.util.Locale; +import java.util.Map; + +import com.alibaba.excel.util.MapUtils; /** * Excel's built-in format conversion.Currently only supports Chinese. @@ -363,6 +366,10 @@ public class BuiltinFormats { // end }; + public static final Map BUILTIN_FORMATS_MAP_CN = buildMap(BUILTIN_FORMATS_CN); + public static final Map BUILTIN_FORMATS_MAP_US = buildMap(BUILTIN_FORMATS_US); + public static final short MIN_CUSTOM_DATA_FORMAT_INDEX = 82; + public static String getBuiltinFormat(Short index, String defaultFormat, Locale locale) { String[] builtinFormat = switchBuiltinFormats(locale); if (index == null || index < 0 || index >= builtinFormat.length) { @@ -371,11 +378,26 @@ public class BuiltinFormats { return builtinFormat[index]; } - private static String[] switchBuiltinFormats(Locale locale) { + public static String[] switchBuiltinFormats(Locale locale) { if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) { return BUILTIN_FORMATS_US; } return BUILTIN_FORMATS_CN; } + public static Map switchBuiltinFormatsMap(Locale locale) { + if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) { + return BUILTIN_FORMATS_MAP_US; + } + return BUILTIN_FORMATS_MAP_CN; + } + + private static Map buildMap(String[] builtinFormats) { + Map map = MapUtils.newHashMapWithExpectedSize(builtinFormats.length); + for (int i = 0; i < builtinFormats.length; i++) { + map.put(builtinFormats[i], (short)i); + } + return map; + } + } diff --git a/src/main/java/com/alibaba/excel/csv/CsvCell.java b/src/main/java/com/alibaba/excel/csv/CsvCell.java deleted file mode 100644 index 84808bf0..00000000 --- a/src/main/java/com/alibaba/excel/csv/CsvCell.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.alibaba.excel.csv; - -import java.time.LocalDateTime; -import java.util.Calendar; -import java.util.Date; - -import org.apache.poi.ss.formula.FormulaParseException; -import org.apache.poi.ss.usermodel.Cell; -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.Hyperlink; -import org.apache.poi.ss.usermodel.RichTextString; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.util.CellAddress; -import org.apache.poi.ss.util.CellRangeAddress; - -/** - * TODO - * - * @author Jiaju Zhuang - */ -public class CsvCell implements Cell { - - private String value; - - @Override - public int getColumnIndex() { - return 0; - } - - @Override - public int getRowIndex() { - return 0; - } - - @Override - public Sheet getSheet() { - return null; - } - - @Override - public Row getRow() { - return null; - } - - @Override - public void setCellType(CellType cellType) { - - } - - @Override - public void setBlank() { - - } - - @Override - public CellType getCellType() { - return null; - } - - @Override - public CellType getCellTypeEnum() { - return null; - } - - @Override - public CellType getCachedFormulaResultType() { - return null; - } - - @Override - public CellType getCachedFormulaResultTypeEnum() { - return null; - } - - @Override - public void setCellValue(double value) { - this.value = value + ""; - } - - @Override - public void setCellValue(Date value) { - this.value = value.toString(); - - } - - @Override - public void setCellValue(LocalDateTime value) { - - } - - @Override - public void setCellValue(Calendar value) { - - } - - @Override - public void setCellValue(RichTextString value) { - - } - - @Override - public void setCellValue(String value) { - this.value = value; - } - - @Override - public void setCellFormula(String formula) throws FormulaParseException, IllegalStateException { - - } - - @Override - public void removeFormula() throws IllegalStateException { - - } - - @Override - public String getCellFormula() { - return null; - } - - @Override - public double getNumericCellValue() { - return 0; - } - - @Override - public Date getDateCellValue() { - return null; - } - - @Override - public LocalDateTime getLocalDateTimeCellValue() { - return null; - } - - @Override - public RichTextString getRichStringCellValue() { - return null; - } - - @Override - public String getStringCellValue() { - return value; - } - - @Override - public void setCellValue(boolean value) { - - } - - @Override - public void setCellErrorValue(byte value) { - - } - - @Override - public boolean getBooleanCellValue() { - return false; - } - - @Override - public byte getErrorCellValue() { - return 0; - } - - @Override - public void setCellStyle(CellStyle style) { - - } - - @Override - public CellStyle getCellStyle() { - return null; - } - - @Override - public void setAsActiveCell() { - - } - - @Override - public CellAddress getAddress() { - return null; - } - - @Override - public void setCellComment(Comment comment) { - - } - - @Override - public Comment getCellComment() { - return null; - } - - @Override - public void removeCellComment() { - - } - - @Override - public Hyperlink getHyperlink() { - return null; - } - - @Override - public void setHyperlink(Hyperlink link) { - - } - - @Override - public void removeHyperlink() { - - } - - @Override - public CellRangeAddress getArrayFormulaRange() { - return null; - } - - @Override - public boolean isPartOfArrayFormulaGroup() { - return false; - } - - - -} diff --git a/src/main/java/com/alibaba/excel/csv/CsvDataFormat.java b/src/main/java/com/alibaba/excel/csv/CsvDataFormat.java deleted file mode 100644 index 2961fbf1..00000000 --- a/src/main/java/com/alibaba/excel/csv/CsvDataFormat.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alibaba.excel.csv; - -import org.apache.poi.ss.usermodel.DataFormat; - -public class CsvDataFormat implements DataFormat { - @Override - public short getFormat(String format) { - return 0; - } - - @Override - public String getFormat(short index) { - return null; - } -} diff --git a/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java new file mode 100644 index 00000000..ef627392 --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java @@ -0,0 +1,291 @@ +package com.alibaba.excel.metadata.csv; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Calendar; +import java.util.Date; + +import com.alibaba.excel.metadata.data.FormulaData; + +import org.apache.poi.ss.SpreadsheetVersion; +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.Hyperlink; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +/** + * csv cell + * + * @author Jiaju Zhuang + */ +public class CsvCell extends CellBase { + + /** + * column index + */ + private Integer columnIndex; + + /** + * cell type + */ + private CellType cellType; + /** + * row + */ + private final CsvRow csvRow; + + /** + * {@link CellType#NUMERIC} + */ + private BigDecimal numberValue; + /** + * {@link CellType#STRING} and {@link CellType#ERROR} {@link CellType#FORMULA} + */ + private String stringValue; + /** + * {@link CellType#BOOLEAN} + */ + private Boolean booleanValue; + + /** + * {@link CellType#NUMERIC} + */ + private LocalDateTime dateValue; + + /** + * formula + */ + private FormulaData formulaData; + + /** + * rich text string + */ + private RichTextString richTextString; + + /** + * style + */ + private CellStyle cellStyle; + + public CsvCell(CsvRow csvRow, Integer columnIndex, CellType cellType) { + this.csvRow = csvRow; + this.columnIndex = columnIndex; + this.cellType = cellType; + } + + @Override + protected void setCellTypeImpl(CellType cellType) { + this.cellType = cellType; + } + + @Override + protected void setCellFormulaImpl(String formula) { + FormulaData formulaData = new FormulaData(); + formulaData.setFormulaValue(formula); + this.formulaData = formulaData; + } + + @Override + protected void removeFormulaImpl() { + this.formulaData = null; + } + + @Override + protected void setCellValueImpl(double value) { + numberValue = BigDecimal.valueOf(value); + } + + @Override + protected void setCellValueImpl(Date value) { + if (value == null) { + return; + } + this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + } + + @Override + protected void setCellValueImpl(LocalDateTime value) { + this.dateValue = value; + } + + @Override + protected void setCellValueImpl(Calendar value) { + if (value == null) { + return; + } + this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + } + + @Override + protected void setCellValueImpl(String value) { + this.stringValue = value; + } + + @Override + protected void setCellValueImpl(RichTextString value) { + richTextString = value; + } + + @Override + protected SpreadsheetVersion getSpreadsheetVersion() { + return null; + } + + @Override + public int getColumnIndex() { + return columnIndex; + } + + @Override + public int getRowIndex() { + return csvRow.getRowNum(); + } + + @Override + public Sheet getSheet() { + return csvRow.getSheet(); + } + + @Override + public Row getRow() { + return csvRow; + } + + @Override + public CellType getCellType() { + return cellType; + } + + @Override + public CellType getCachedFormulaResultType() { + return getCellType(); + } + + @Override + public CellType getCachedFormulaResultTypeEnum() { + return getCellType(); + } + + @Override + public String getCellFormula() { + if (formulaData == null) { + return null; + } + return formulaData.getFormulaValue(); + } + + @Override + public double getNumericCellValue() { + if (numberValue == null) { + return 0; + } + return numberValue.doubleValue(); + } + + @Override + public Date getDateCellValue() { + if (dateValue == null) { + return null; + } + return Date.from(dateValue.atZone(ZoneId.systemDefault()).toInstant()); + } + + @Override + public LocalDateTime getLocalDateTimeCellValue() { + return dateValue; + } + + @Override + public RichTextString getRichStringCellValue() { + return richTextString; + } + + @Override + public String getStringCellValue() { + return stringValue; + } + + @Override + public void setCellValue(boolean value) { + this.booleanValue = value; + } + + @Override + public void setCellErrorValue(byte value) { + this.numberValue = BigDecimal.valueOf(value); + } + + @Override + public boolean getBooleanCellValue() { + return booleanValue; + } + + @Override + public byte getErrorCellValue() { + if (numberValue == null) { + return 0; + } + return numberValue.byteValue(); + } + + @Override + public void setCellStyle(CellStyle style) { + this.cellStyle = style; + } + + @Override + public CellStyle getCellStyle() { + return cellStyle; + } + + @Override + public void setAsActiveCell() { + + } + + @Override + public void setCellComment(Comment comment) { + + } + + @Override + public Comment getCellComment() { + return null; + } + + @Override + public void removeCellComment() { + + } + + @Override + public Hyperlink getHyperlink() { + return null; + } + + @Override + public void setHyperlink(Hyperlink link) { + + } + + @Override + public void removeHyperlink() { + + } + + @Override + public CellRangeAddress getArrayFormulaRange() { + return null; + } + + @Override + public boolean isPartOfArrayFormulaGroup() { + return false; + } +} diff --git a/src/main/java/com/alibaba/excel/csv/CsvCellStyle.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java similarity index 93% rename from src/main/java/com/alibaba/excel/csv/CsvCellStyle.java rename to src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java index 318b42a4..8c8fe981 100644 --- a/src/main/java/com/alibaba/excel/csv/CsvCellStyle.java +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java @@ -1,4 +1,6 @@ -package com.alibaba.excel.csv; +package com.alibaba.excel.metadata.csv; + +import com.alibaba.excel.metadata.data.DataFormatData; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.CellStyle; @@ -9,11 +11,26 @@ import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.VerticalAlignment; /** - * TODO + * csv cell style * * @author Jiaju Zhuang */ public class CsvCellStyle implements CellStyle { + + /** + * data format + */ + private DataFormatData dataFormatData; + + /** + * index + */ + private Short index; + + public CsvCellStyle(Short index) { + this.index = index; + } + @Override public short getIndex() { return 0; diff --git a/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java new file mode 100644 index 00000000..1dcc7c2e --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java @@ -0,0 +1,65 @@ +package com.alibaba.excel.metadata.csv; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; + +import org.apache.poi.ss.usermodel.DataFormat; + +/** + * format data + * + * @author Jiaju Zhuang + */ +public class CsvDataFormat implements DataFormat { + /** + * It is stored in both map and list for easy retrieval + */ + private final Map formatMap; + private final List formatList; + + /** + * Excel's built-in format conversion. + */ + private final Map builtinFormatsMap; + private final String[] builtinFormats; + + public CsvDataFormat(Locale locale) { + formatMap = MapUtils.newHashMap(); + formatList = ListUtils.newArrayList(); + builtinFormatsMap = BuiltinFormats.switchBuiltinFormatsMap(locale); + builtinFormats = BuiltinFormats.switchBuiltinFormats(locale); + } + + @Override + public short getFormat(String format) { + Short index = builtinFormatsMap.get(format); + if (index != null) { + return index; + } + index = formatMap.get(format); + if (index != null) { + return index; + } + formatList.add(format); + index = (short)formatList.size(); + formatMap.put(format, index); + return index; + } + + @Override + public String getFormat(short index) { + if (index < BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX) { + return builtinFormats[index]; + } + int actualIndex = index - BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX; + if (actualIndex >= 0 && actualIndex < formatList.size()) { + return formatList.get(actualIndex); + } + return null; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java new file mode 100644 index 00000000..1c54b0c3 --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java @@ -0,0 +1,68 @@ +package com.alibaba.excel.metadata.csv; + +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.RichTextString; + +/** + * rich text string + * + * @author Jiaju Zhuang + */ +public class CsvRichTextString implements RichTextString { + /** + * string + */ + private final String string; + + public CsvRichTextString(String string) { + this.string = string; + } + + @Override + public void applyFont(int startIndex, int endIndex, short fontIndex) { + + } + + @Override + public void applyFont(int startIndex, int endIndex, Font font) { + + } + + @Override + public void applyFont(Font font) { + + } + + @Override + public void clearFormatting() { + + } + + @Override + public String getString() { + return string; + } + + @Override + public int length() { + if (string == null) { + return 0; + } + return string.length(); + } + + @Override + public int numFormattingRuns() { + return 0; + } + + @Override + public int getIndexOfFormattingRun(int index) { + return 0; + } + + @Override + public void applyFont(short fontIndex) { + + } +} diff --git a/src/main/java/com/alibaba/excel/csv/CsvRow.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java similarity index 59% rename from src/main/java/com/alibaba/excel/csv/CsvRow.java rename to src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java index 16129263..d7248313 100644 --- a/src/main/java/com/alibaba/excel/csv/CsvRow.java +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java @@ -1,9 +1,10 @@ -package com.alibaba.excel.csv; +package com.alibaba.excel.metadata.csv; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.utils.Lists; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; @@ -11,64 +12,98 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; /** - * TODO + * csv row * * @author Jiaju Zhuang */ public class CsvRow implements Row { - public List list = new ArrayList<>(); + /** + * cell list + */ + private final List cellList; + + /** + * sheet + */ + private final CsvSheet csvSheet; + /** + * row index + */ + private Integer rowIndex; + + /** + * style + */ + private CellStyle cellStyle; + + public CsvRow(CsvSheet csvSheet, Integer rowIndex) { + cellList = Lists.newArrayList(); + this.csvSheet = csvSheet; + this.rowIndex = rowIndex; + } @Override public Cell createCell(int column) { - CsvCell cell = new CsvCell(); - list.add(cell); + CsvCell cell = new CsvCell(this, column, null); + cellList.add(cell); return cell; } @Override public Cell createCell(int column, CellType type) { - return null; + CsvCell cell = new CsvCell(this, column, type); + cellList.add(cell); + return cell; } @Override public void removeCell(Cell cell) { - + cellList.remove(cell); } @Override public void setRowNum(int rowNum) { - + this.rowIndex = rowNum; } @Override public int getRowNum() { - return 0; + return rowIndex; } @Override public Cell getCell(int cellnum) { - return list.get(cellnum); + if (cellnum >= cellList.size()) { + return null; + } + return cellList.get(cellnum - 1); } @Override public Cell getCell(int cellnum, MissingCellPolicy policy) { - return null; + return getCell(cellnum); } @Override public short getFirstCellNum() { + if (CollectionUtils.isEmpty(cellList)) { + return -1; + } return 0; } @Override public short getLastCellNum() { - return (short)list.size(); + if (CollectionUtils.isEmpty(cellList)) { + return -1; + } + return (short)cellList.size(); } @Override public int getPhysicalNumberOfCells() { - return 0; + return getRowNum(); } @Override @@ -108,22 +143,22 @@ public class CsvRow implements Row { @Override public CellStyle getRowStyle() { - return null; + return cellStyle; } @Override public void setRowStyle(CellStyle style) { - + this.cellStyle = style; } @Override public Iterator cellIterator() { - return null; + return (Iterator)(Iterator)cellList.iterator(); } @Override public Sheet getSheet() { - return null; + return csvSheet; } @Override @@ -143,6 +178,6 @@ public class CsvRow implements Row { @Override public Iterator iterator() { - return null; + return cellIterator(); } } diff --git a/src/main/java/com/alibaba/excel/csv/CsvSheet.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java similarity index 77% rename from src/main/java/com/alibaba/excel/csv/CsvSheet.java rename to src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java index db21787b..07f09e73 100644 --- a/src/main/java/com/alibaba/excel/csv/CsvSheet.java +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java @@ -1,6 +1,9 @@ -package com.alibaba.excel.csv; +package com.alibaba.excel.metadata.csv; +import java.io.Closeable; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -9,10 +12,11 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.util.ListUtils; +import lombok.Data; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.poi.ss.usermodel.AutoFilter; @@ -36,62 +40,122 @@ import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.PaneInformation; /** - * TODO + * csv sheet * * @author Jiaju Zhuang */ -public class CsvSheet implements Sheet { - - public static int rowCacheCount = 500; - public static Charset defaultCharset = Charset.defaultCharset(); - - public CSVPrinter csvPrinter; - - private int lastRowNum = -1; - - private List rowCache = ListUtils.newArrayListWithExpectedSize(rowCacheCount); - - public CsvSheet(File file) throws IOException { - csvPrinter = CSVFormat.DEFAULT.print(file, defaultCharset); - } - - public CsvSheet(OutputStream outputStream) throws IOException { - csvPrinter = CSVFormat.DEFAULT.print(new OutputStreamWriter(outputStream)); +@Data +public class CsvSheet implements Sheet, Closeable { + + /** + * workbook + */ + private CsvWorkbook csvWorkbook; + /** + * output + */ + private Appendable out; + /** + * row cache + */ + private Integer rowCacheCount; + /** + * format + */ + public CSVFormat csvFormat; + + /** + * last row index + */ + private int lastRowIndex; + /** + * row cache + */ + private List rowCache; + /** + * csv printer + */ + private CSVPrinter csvPrinter; + + public CsvSheet(CsvWorkbook csvWorkbook, File file, Charset charset) throws FileNotFoundException { + this.csvWorkbook = csvWorkbook; + this.out = new OutputStreamWriter(new FileOutputStream(file), charset); + this.rowCacheCount = 500; + this.csvFormat = CSVFormat.DEFAULT; + this.lastRowIndex = -1; + } + + public CsvSheet(CsvWorkbook csvWorkbook, OutputStream outputStream) { + this.csvWorkbook = csvWorkbook; + this.out = new OutputStreamWriter(outputStream); + this.rowCacheCount = 500; + this.csvFormat = CSVFormat.DEFAULT; + this.lastRowIndex = -1; + } + + public CsvSheet(CsvWorkbook csvWorkbook, Appendable out) { + this.csvWorkbook = csvWorkbook; + this.out = out; + this.rowCacheCount = 500; + this.csvFormat = CSVFormat.DEFAULT; + this.lastRowIndex = -1; } @Override public Row createRow(int rownum) { - lastRowNum++; - assert rownum == lastRowNum : "csv create row must be in order."; + // Initialize the data when the row is first created + initSheet(); + + lastRowIndex++; + assert rownum == lastRowIndex : "csv create row must be in order."; printData(); - CsvRow csvRow = new CsvRow(); + CsvRow csvRow = new CsvRow(this, rownum); rowCache.add(csvRow); return csvRow; } + private void initSheet() { + if (csvPrinter != null) { + return; + } + rowCache = ListUtils.newArrayListWithExpectedSize(rowCacheCount); + try { + csvPrinter = csvFormat.print(out); + } catch (IOException e) { + throw new ExcelGenerateException(e); + } + } + @Override public void removeRow(Row row) { - + throw new UnsupportedOperationException("csv cannot move row."); } @Override public Row getRow(int rownum) { - return null; + int actualRowIndex = rownum - (lastRowIndex - rowCache.size()) - 1; + if (actualRowIndex < 0 || actualRowIndex > rowCache.size() - 1) { + throw new UnsupportedOperationException("The current data does not exist or has been flushed to disk\n."); + } + return rowCache.get(actualRowIndex); } @Override public int getPhysicalNumberOfRows() { - return 0; + return lastRowIndex - rowCache.size(); } @Override public int getFirstRowNum() { + if (lastRowIndex < 0) { + return -1; + } return 0; } @Override public int getLastRowNum() { - return 0; + return lastRowIndex; } @Override @@ -226,7 +290,7 @@ public class CsvSheet implements Sheet { @Override public Iterator rowIterator() { - return null; + return (Iterator)(Iterator)rowCache.iterator(); } @Override @@ -556,7 +620,7 @@ public class CsvSheet implements Sheet { @Override public Workbook getWorkbook() { - return null; + return csvWorkbook; } @Override @@ -657,23 +721,39 @@ public class CsvSheet implements Sheet { @Override public Iterator iterator() { - return null; + return rowIterator(); + } + + @Override + public void close() throws IOException { + // Avoid empty sheets + initSheet(); + + flushData(); + csvPrinter.flush(); + csvPrinter.close(); } public void printData() { if (rowCache.size() >= rowCacheCount) { flushData(); - rowCache.clear(); } } public void flushData() { try { for (CsvRow row : rowCache) { - csvPrinter.printRecord(row.list.stream().map(CsvCell::getStringCellValue).collect(Collectors.toList())); + Iterator cellIterator = row.cellIterator(); + while (cellIterator.hasNext()) { + CsvCell csvCell = (CsvCell)cellIterator.next(); + csvPrinter.print(csvCell.getStringCellValue()); + } + csvPrinter.println(); } + rowCache.clear(); } catch (IOException e) { - e.printStackTrace(); + throw new ExcelGenerateException(e); } } + } diff --git a/src/main/java/com/alibaba/excel/csv/CsvWorkbook.java b/src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java similarity index 80% rename from src/main/java/com/alibaba/excel/csv/CsvWorkbook.java rename to src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java index 082e770d..ae8a3e50 100644 --- a/src/main/java/com/alibaba/excel/csv/CsvWorkbook.java +++ b/src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java @@ -1,13 +1,13 @@ -package com.alibaba.excel.csv; +package com.alibaba.excel.metadata.csv; -import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.List; +import java.util.Locale; import lombok.Data; +import org.apache.commons.compress.utils.Lists; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.udf.UDFFinder; import org.apache.poi.ss.usermodel.CellStyle; @@ -22,21 +22,37 @@ import org.apache.poi.ss.usermodel.SheetVisibility; import org.apache.poi.ss.usermodel.Workbook; /** - * TODO + * csv workbook * * @author Jiaju Zhuang */ @Data public class CsvWorkbook implements Workbook { - + /** + * output + */ + private Appendable out; + /** + * locale + */ + private Locale locale; + + /** + * data format + */ + private CsvDataFormat csvDataFormat; + /** + * sheet + */ private CsvSheet csvSheet; + /** + * cell style + */ + private List csvCellStyleList; - public CsvWorkbook(InputStream inputStream, File file) { - try { - this.csvSheet = new CsvSheet(file); - } catch (IOException e) { - e.printStackTrace(); - } + public CsvWorkbook(Appendable out, Locale locale) { + this.out = out; + this.locale = locale; } @Override @@ -91,11 +107,15 @@ public class CsvWorkbook implements Workbook { @Override public Sheet createSheet() { + assert csvSheet == null : "CSV repeat creation is not allowed."; + csvSheet = new CsvSheet(this, out); return csvSheet; } @Override public Sheet createSheet(String sheetname) { + assert csvSheet == null : "CSV repeat creation is not allowed."; + csvSheet = new CsvSheet(this, out); return csvSheet; } @@ -116,12 +136,13 @@ public class CsvWorkbook implements Workbook { @Override public Sheet getSheetAt(int index) { - return null; + assert index == 0 : "CSV exists only in one sheet."; + return csvSheet; } @Override public Sheet getSheet(String name) { - return null; + return csvSheet; } @Override @@ -162,24 +183,30 @@ public class CsvWorkbook implements Workbook { @Override public CellStyle createCellStyle() { - return new CsvCellStyle(); + if (csvCellStyleList == null) { + csvCellStyleList = Lists.newArrayList(); + } + CsvCellStyle csvCellStyle = new CsvCellStyle((short)csvCellStyleList.size()); + csvCellStyleList.add(csvCellStyle); + return csvCellStyle; } @Override public int getNumCellStyles() { - return 0; + return csvCellStyleList.size(); } @Override public CellStyle getCellStyleAt(int idx) { - return null; + if (idx < 0 || idx >= csvCellStyleList.size()) { + return null; + } + return csvCellStyleList.get(idx); } @Override public void write(OutputStream stream) throws IOException { - csvSheet.flushData(); - csvSheet.csvPrinter.flush(); - csvSheet.csvPrinter.close(); + csvSheet.close(); } @Override @@ -274,7 +301,11 @@ public class CsvWorkbook implements Workbook { @Override public DataFormat createDataFormat() { - return new CsvDataFormat(); + if (csvDataFormat != null) { + return csvDataFormat; + } + csvDataFormat = new CsvDataFormat(locale); + return csvDataFormat; } @Override diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index 0760e895..3fb6575e 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -1,8 +1,10 @@ package com.alibaba.excel.util; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; -import com.alibaba.excel.csv.CsvWorkbook; +import com.alibaba.excel.metadata.csv.CsvWorkbook; import com.alibaba.excel.metadata.data.DataFormatData; import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; @@ -67,12 +69,15 @@ public class WorkBookUtil { return; case CSV: CsvWorkbook csvWorkbook; - if (writeWorkbookHolder.getTempTemplateInputStream() != null) { - csvWorkbook = new CsvWorkbook(writeWorkbookHolder.getTempTemplateInputStream(), - writeWorkbookHolder.getFile()); + if (writeWorkbookHolder.getFile() != null) { + csvWorkbook = new CsvWorkbook( + new OutputStreamWriter(new FileOutputStream(writeWorkbookHolder.getFile()), + writeWorkbookHolder.getFileCharset()), + writeWorkbookHolder.getGlobalConfiguration().getLocale()); } else { - csvWorkbook = new CsvWorkbook(null, writeWorkbookHolder.getFile()); + csvWorkbook = new CsvWorkbook(null, writeWorkbookHolder.getGlobalConfiguration().getLocale()); } + CsvWorkbook csvWorkbook = new CsvWorkbook(writeWorkbookHolder); writeWorkbookHolder.setCachedWorkbook(csvWorkbook); writeWorkbookHolder.setWorkbook(csvWorkbook); return; diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index a2a0d113..cb29ec22 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -7,6 +7,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; @@ -64,6 +65,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * If 'outputStream' and 'file' all not empty,file first */ private File file; + /** + * charset of final output file + */ + private Charset fileCharset; /** * Final output stream */ @@ -152,11 +157,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { } else { this.autoCloseStream = writeWorkbook.getAutoCloseStream(); } - try { - copyTemplate(); - } catch (IOException e) { - throw new ExcelGenerateException("Copy template failure.", e); - } if (writeWorkbook.getExcelType() == null) { boolean isXls = (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) || (writeWorkbook.getTemplateFile() != null @@ -176,6 +176,11 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { } else { this.excelType = writeWorkbook.getExcelType(); } + try { + copyTemplate(); + } catch (IOException e) { + throw new ExcelGenerateException("Copy template failure.", e); + } if (writeWorkbook.getMandatoryUseInputStream() == null) { this.mandatoryUseInputStream = Boolean.FALSE; } else { @@ -203,6 +208,9 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { if (writeWorkbook.getTemplateFile() == null && writeWorkbook.getTemplateInputStream() == null) { return; } + if (this.excelType == ExcelTypeEnum.CSV) { + throw new ExcelGenerateException("csv cannot use template."); + } byte[] templateFileByte = null; if (writeWorkbook.getTemplateFile() != null) { templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java index 4489f1c1..54fa2700 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java @@ -4,26 +4,27 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import org.junit.BeforeClass; -import org.junit.Test; - import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.util.DateUtils; +import org.junit.BeforeClass; +import org.junit.Test; + /** - * * @author Jiaju Zhuang */ public class AnnotationDataTest { private static File file07; private static File file03; + private static File fileCsv; @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("annotation07.xlsx"); file03 = TestFileUtil.createNewFile("annotation03.xls"); + fileCsv = TestFileUtil.createNewFile("annotation.csv"); } @Test @@ -36,6 +37,11 @@ public class AnnotationDataTest { readAndWrite(file03); } + @Test + public void t02ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + private void readAndWrite(File file) throws Exception { EasyExcel.write().file(file).head(AnnotationData.class).sheet().doWrite(data()); EasyExcel.read().file(file).head(AnnotationData.class).registerReadListener(new AnnotationDataListener()) @@ -43,7 +49,7 @@ public class AnnotationDataTest { } private List data() throws Exception { - List list = new ArrayList(); + List list = new ArrayList<>(); AnnotationData data = new AnnotationData(); data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); data.setNumber(99.99);