From 3c76802bd60ba3b866e50d3213a23a656bd7d616 Mon Sep 17 00:00:00 2001 From: zhuangjiaju Date: Wed, 31 Jul 2019 18:04:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BD=AC=E6=8D=A2=E5=99=A8?= =?UTF-8?q?=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/analysis/v03/XlsSaxAnalyser.java | 10 ++-- .../handlers/BlankOrErrorRecordHandler.java | 2 +- .../analysis/v07/XlsxRowResultHolder.java | 11 +--- .../v07/handlers/DefaultCellHandler.java | 25 ++++----- .../excel/context/WriteContextImpl.java | 4 ++ .../converters/DefaultConverterLoader.java | 20 ++++--- .../BigDecimalBooleanConverter.java | 2 +- .../shortconverter/ShortBooleanConverter.java | 2 +- .../alibaba/excel/enums/CellDataTypeEnum.java | 7 ++- .../java/com/alibaba/excel/metadata/Head.java | 24 +++++--- .../metadata/property/ExcelHeadProperty.java | 7 ++- .../listener/ModelBuildEventListener.java | 21 ++++--- .../listener/event/AnalysisFinishEvent.java | 4 +- .../event/EachRowAnalysisFinishEvent.java | 9 +-- .../metadata/holder/AbstractReadHolder.java | 16 +++--- .../alibaba/excel/write/ExcelBuilderImpl.java | 47 +++++++++------- .../test/core/converter/ConverterData.java | 3 + .../core/converter/ConverterDataListener.java | 1 + .../core/converter/ConverterDataTest.java | 29 +++++++++- .../core/converter/ReadAllConverterData.java | 45 +++++++++++++++ .../ReadAllConverterDataListener.java | 46 +++++++++++++++ .../core/converter/ReadConverterData.java | 53 ------------------ .../{nohead => head}/NoHeadData07Test.java | 4 +- .../test/core/simple/SimpleDataTest.java | 15 +++-- .../easyexcel/test/util/TestFileUtil.java | 7 ++- src/test/resources/converter/converter03.xls | Bin 0 -> 20480 bytes src/test/resources/converter/converter07.xlsx | Bin 0 -> 10692 bytes 27 files changed, 259 insertions(+), 155 deletions(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java delete mode 100644 src/test/java/com/alibaba/easyexcel/test/core/converter/ReadConverterData.java rename src/test/java/com/alibaba/easyexcel/test/core/{nohead => head}/NoHeadData07Test.java (95%) create mode 100644 src/test/resources/converter/converter03.xls create mode 100644 src/test/resources/converter/converter07.xlsx diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index 748e325..340260c 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.TreeMap; import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder; import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; @@ -62,7 +64,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { */ private EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener; private FormatTrackingHSSFListener formatListener; - private List records; + private Map records; private List sheets = new ArrayList(); private HSSFWorkbook stubWorkbook; private List recordHandlers = new ArrayList(); @@ -70,7 +72,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { public XlsSaxAnalyser(AnalysisContext context) throws IOException { this.analysisContext = context; - this.records = new ArrayList(); + this.records = new TreeMap(); ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); if (readWorkbookHolder.getFile() != null) { this.fs = new POIFSFileSystem(readWorkbookHolder.getFile()); @@ -114,7 +116,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { private void init() { lastRowNumber = 0; lastColumnNumber = 0; - records = new ArrayList(); + records = new TreeMap(); sheets = new ArrayList(); buildXlsRecordHandlers(); } @@ -131,7 +133,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { thisColumn = handler.getColumn(); cellData = handler.getCellData(); if (cellData != null) { - records.add(cellData); + records.put(thisColumn, cellData); } break; } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankOrErrorRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankOrErrorRecordHandler.java index c858e37..f64d437 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankOrErrorRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankOrErrorRecordHandler.java @@ -31,7 +31,7 @@ public class BlankOrErrorRecordHandler extends AbstractXlsRecordHandler { BoolErrRecord ber = (BoolErrRecord)record; this.row = ber.getRow(); this.column = ber.getColumn(); - this.cellData = new CellData(CellDataTypeEnum.EMPTY); + this.cellData = new CellData(ber.getBooleanValue()); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java index 90c1ea9..953d45e 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java @@ -1,6 +1,6 @@ package com.alibaba.excel.analysis.v07; -import java.util.List; +import java.util.Map; import com.alibaba.excel.metadata.CellData; @@ -27,12 +27,5 @@ public interface XlsxRowResultHolder { * * @return */ - List getCurRowContent(); - - /** - * get column size - * - * @return - */ - int getColumnSize(); + Map getCurRowContent(); } diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java index d64f315..3f376b4 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java @@ -6,8 +6,8 @@ import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_TAG; import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TAG; import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TYPE_TAG; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; +import java.util.TreeMap; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.xml.sax.Attributes; @@ -34,7 +34,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder private String currentCellIndex; private int curRow; private int curCol; - private List curRowContent = new ArrayList(); + private Map curRowContent = new TreeMap(); private CellData currentCellData; public DefaultCellHandler(AnalysisContext analysisContext) { @@ -43,7 +43,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder @Override public void clearResult() { - curRowContent.clear(); + curRowContent = new TreeMap(); } @Override @@ -67,6 +67,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder curCol = PositionUtils.getCol(currentCellIndex); // t="s" ,it's means String + // t="str" ,it's means String,but does not need to be read in the 'sharedStrings.xml' // t="inlineStr" ,it's means String // t="b" ,it's means Boolean // t="e" ,it's means Error @@ -92,8 +93,10 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder stringValue = stringValue.trim(); } currentCellData.setStringValue(stringValue); + } else if (currentCellData.getType() == CellDataTypeEnum.DIRECT_STRING) { + currentCellData.setType(CellDataTypeEnum.STRING); } - curRowContent.set(curCol, currentCellData); + curRowContent.put(curCol, currentCellData); } // This is a special form of string if (CELL_INLINE_STRING_VALUE_TAG.equals(name)) { @@ -103,7 +106,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder stringValue = stringValue.trim(); } currentCellData.setStringValue(stringValue); - curRowContent.set(curCol, currentCellData); + curRowContent.put(curCol, currentCellData); } } @@ -121,6 +124,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder } CellDataTypeEnum oldType = currentCellData.getType(); switch (oldType) { + case DIRECT_STRING: case STRING: case ERROR: currentCellData.setStringValue(currentCellValue); @@ -139,13 +143,8 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder } @Override - public List getCurRowContent() { - return this.curRowContent; - } - - @Override - public int getColumnSize() { - return this.curCol; + public Map getCurRowContent() { + return curRowContent; } } diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index db7c080..2b1f7d7 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -352,6 +352,10 @@ public class WriteContextImpl implements WriteContext { if (writeWorkbookHolder.getTemplateInputStream() != null) { writeWorkbookHolder.getTemplateInputStream().close(); } + } else { + if (writeWorkbookHolder.getFile() != null && writeWorkbookHolder.getOutputStream() != null) { + writeWorkbookHolder.getOutputStream().close(); + } } } catch (IOException e) { throw new ExcelGenerateException("Can not close IO", e); diff --git a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java index 7a6519e..c1155e5 100644 --- a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java +++ b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java @@ -14,23 +14,29 @@ import com.alibaba.excel.converters.byteconverter.ByteNumberConverter; import com.alibaba.excel.converters.byteconverter.ByteStringConverter; import com.alibaba.excel.converters.date.DateNumberConverter; import com.alibaba.excel.converters.date.DateStringConverter; +import com.alibaba.excel.converters.doubleconverter.DoubleBooleanConverter; import com.alibaba.excel.converters.doubleconverter.DoubleNumberConverter; import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; +import com.alibaba.excel.converters.floatconverter.FloatBooleanConverter; import com.alibaba.excel.converters.floatconverter.FloatNumberConverter; import com.alibaba.excel.converters.floatconverter.FloatStringConverter; +import com.alibaba.excel.converters.integer.IntegerBooleanConverter; import com.alibaba.excel.converters.integer.IntegerNumberConverter; import com.alibaba.excel.converters.integer.IntegerStringConverter; +import com.alibaba.excel.converters.longconverter.LongBooleanConverter; import com.alibaba.excel.converters.longconverter.LongNumberConverter; import com.alibaba.excel.converters.longconverter.LongStringConverter; +import com.alibaba.excel.converters.shortconverter.ShortBooleanConverter; import com.alibaba.excel.converters.shortconverter.ShortNumberConverter; import com.alibaba.excel.converters.shortconverter.ShortStringConverter; +import com.alibaba.excel.converters.string.StringBooleanConverter; import com.alibaba.excel.converters.string.StringErrorConverter; import com.alibaba.excel.converters.string.StringNumberConverter; import com.alibaba.excel.converters.string.StringStringConverter; /** * Load default handler - * + * * @author zhuangjiaju */ public class DefaultConverterLoader { @@ -80,27 +86,27 @@ public class DefaultConverterLoader { putReadConverter(converterMap, new DateNumberConverter()); putReadConverter(converterMap, new DateStringConverter()); - putReadConverter(converterMap, new DoubleNumberConverter()); + putReadConverter(converterMap, new DoubleBooleanConverter()); putReadConverter(converterMap, new DoubleNumberConverter()); putReadConverter(converterMap, new DoubleStringConverter()); - putReadConverter(converterMap, new FloatNumberConverter()); + putReadConverter(converterMap, new FloatBooleanConverter()); putReadConverter(converterMap, new FloatNumberConverter()); putReadConverter(converterMap, new FloatStringConverter()); - putReadConverter(converterMap, new IntegerNumberConverter()); + putReadConverter(converterMap, new IntegerBooleanConverter()); putReadConverter(converterMap, new IntegerNumberConverter()); putReadConverter(converterMap, new IntegerStringConverter()); - putReadConverter(converterMap, new LongNumberConverter()); + putReadConverter(converterMap, new LongBooleanConverter()); putReadConverter(converterMap, new LongNumberConverter()); putReadConverter(converterMap, new LongStringConverter()); - putReadConverter(converterMap, new ShortNumberConverter()); + putReadConverter(converterMap, new ShortBooleanConverter()); putReadConverter(converterMap, new ShortNumberConverter()); putReadConverter(converterMap, new ShortStringConverter()); - putReadConverter(converterMap, new StringNumberConverter()); + putReadConverter(converterMap, new StringBooleanConverter()); putReadConverter(converterMap, new StringNumberConverter()); putReadConverter(converterMap, new StringStringConverter()); putReadConverter(converterMap, new StringErrorConverter()); diff --git a/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java b/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java index 7d35d39..1ff6171 100644 --- a/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java +++ b/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java @@ -17,7 +17,7 @@ public class BigDecimalBooleanConverter implements Converter { @Override public Class supportJavaTypeKey() { - return Byte.class; + return BigDecimal.class; } @Override diff --git a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java index 65d5316..40d7507 100644 --- a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java +++ b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java @@ -17,7 +17,7 @@ public class ShortBooleanConverter implements Converter { @Override public Class supportJavaTypeKey() { - return Long.class; + return Short.class; } @Override diff --git a/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java b/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java index 72ce70a..b12b0bf 100644 --- a/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java +++ b/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java @@ -11,11 +11,15 @@ import com.alibaba.excel.util.StringUtils; * @author zhuangjiaju */ public enum CellDataTypeEnum { - /** * string */ STRING, + /** + * This type of data does not need to be read in the 'sharedStrings.xml', it is only used for overuse, and the data + * will be stored as a {@link #STRING} + */ + DIRECT_STRING, /** * number */ @@ -36,6 +40,7 @@ public enum CellDataTypeEnum { private static final Map TYPE_ROUTING_MAP = new HashMap(16); static { TYPE_ROUTING_MAP.put("s", STRING); + TYPE_ROUTING_MAP.put("str", DIRECT_STRING); TYPE_ROUTING_MAP.put("inlineStr", STRING); TYPE_ROUTING_MAP.put("e", ERROR); TYPE_ROUTING_MAP.put("b", BOOLEAN); diff --git a/src/main/java/com/alibaba/excel/metadata/Head.java b/src/main/java/com/alibaba/excel/metadata/Head.java index 00ba216..3e0f912 100644 --- a/src/main/java/com/alibaba/excel/metadata/Head.java +++ b/src/main/java/com/alibaba/excel/metadata/Head.java @@ -27,20 +27,17 @@ public class Head { * Whether index is specified */ private Boolean forceIndex; + /** + * Whether to specify a name + */ + private Boolean forceName; /** * column with */ private ColumnWidthProperty columnWidthProperty; - public Head(Integer columnIndex, String fieldName, String headName) { - this.columnIndex = columnIndex; - this.fieldName = fieldName; - headNameList = new ArrayList(); - headNameList.add(headName); - this.forceIndex = Boolean.FALSE; - } - - public Head(Integer columnIndex, String fieldName, List headNameList, Boolean forceIndex) { + public Head(Integer columnIndex, String fieldName, List headNameList, Boolean forceIndex, + Boolean forceName) { this.columnIndex = columnIndex; this.fieldName = fieldName; if (headNameList == null) { @@ -48,6 +45,7 @@ public class Head { } this.headNameList = headNameList; this.forceIndex = forceIndex; + this.forceName = forceName; } public Integer getColumnIndex() { @@ -89,4 +87,12 @@ public class Head { public void setForceIndex(Boolean forceIndex) { this.forceIndex = forceIndex; } + + public Boolean getForceName() { + return forceName; + } + + public void setForceName(Boolean forceName) { + this.forceName = forceName; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 4b8fcf4..e939150 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -60,7 +60,7 @@ public class ExcelHeadProperty { headRowNumber = 0; if (head != null && !head.isEmpty()) { for (int i = 0; i < head.size(); i++) { - headMap.put(i, new Head(i, null, head.get(i), Boolean.FALSE)); + headMap.put(i, new Head(i, null, head.get(i), Boolean.FALSE, Boolean.TRUE)); contentPropertyMap.put(i, null); } headKind = HeadKindEnum.STRING; @@ -155,15 +155,18 @@ public class ExcelHeadProperty { private void initOneColumnProperty(int index, Field field, Boolean forceIndex) { ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); List tmpHeadList = new ArrayList(); + Boolean forceName = Boolean.TRUE; if (excelProperty != null) { tmpHeadList = Arrays.asList(excelProperty.value()); } else { + forceName = Boolean.FALSE; tmpHeadList.add(field.getName()); } if (tmpHeadList.isEmpty() || StringUtils.isEmpty(tmpHeadList.get(0))) { + forceName = Boolean.FALSE; tmpHeadList.add(field.getName()); } - Head head = new Head(index, field.getName(), tmpHeadList, forceIndex); + Head head = new Head(index, field.getName(), tmpHeadList, forceIndex, forceName); ExcelContentProperty excelContentProperty = new ExcelContentProperty(); if (excelProperty != null && excelProperty.converter() != null) { Class convertClazz = excelProperty.converter(); diff --git a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java index 8f14702..7285808 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -26,28 +26,28 @@ import net.sf.cglib.beans.BeanMap; * * @author jipengfei */ -public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener> { +public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener> { @Override - public void invoke(List data, AnalysisContext context) { + public void invoke(Map cellDataMap, AnalysisContext context) { ReadHolder currentReadHolder = context.currentReadHolder(); if (HeadKindEnum.CLASS.equals(currentReadHolder.excelReadHeadProperty().getHeadKind())) { - context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(data, currentReadHolder)); + context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder)); return; } - context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(data, currentReadHolder)); + context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, currentReadHolder)); } - private Object buildStringList(List data, ReadHolder currentReadHolder) { + private Object buildStringList(Map cellDataMap, ReadHolder currentReadHolder) { List list = new ArrayList(); - for (CellData cellData : data) { + for (CellData cellData : cellDataMap.values()) { list.add((String)convertValue(cellData, String.class, null, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); } return list; } - private Object buildUserModel(List data, ReadHolder currentReadHolder) { + private Object buildUserModel(Map cellDataMap, ReadHolder currentReadHolder) { ExcelReadHeadProperty excelReadHeadProperty = currentReadHolder.excelReadHeadProperty(); Object resultModel; try { @@ -61,10 +61,10 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener Map contentPropertyMap = excelReadHeadProperty.getContentPropertyMap(); for (Map.Entry entry : headMap.entrySet()) { Integer index = entry.getKey(); - if (index >= data.size()) { + if (index >= cellDataMap.size()) { continue; } - CellData cellData = data.get(index); + CellData cellData = cellDataMap.get(index); if (cellData.getType() == CellDataTypeEnum.EMPTY) { continue; } @@ -81,6 +81,9 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener private Object convertValue(CellData cellData, Class clazz, ExcelContentProperty contentProperty, Map converterMap, GlobalConfiguration globalConfiguration) { + if (clazz == CellData.class) { + return cellData; + } Converter converter = null; if (contentProperty != null) { converter = contentProperty.getConverter(); diff --git a/src/main/java/com/alibaba/excel/read/listener/event/AnalysisFinishEvent.java b/src/main/java/com/alibaba/excel/read/listener/event/AnalysisFinishEvent.java index c73b328..9749cf0 100644 --- a/src/main/java/com/alibaba/excel/read/listener/event/AnalysisFinishEvent.java +++ b/src/main/java/com/alibaba/excel/read/listener/event/AnalysisFinishEvent.java @@ -1,6 +1,6 @@ package com.alibaba.excel.read.listener.event; -import java.util.List; +import java.util.Map; import com.alibaba.excel.metadata.CellData; @@ -16,5 +16,5 @@ public interface AnalysisFinishEvent { * * @return */ - List getAnalysisResult(); + Map getAnalysisResult(); } diff --git a/src/main/java/com/alibaba/excel/read/listener/event/EachRowAnalysisFinishEvent.java b/src/main/java/com/alibaba/excel/read/listener/event/EachRowAnalysisFinishEvent.java index 0a5a945..879a59a 100644 --- a/src/main/java/com/alibaba/excel/read/listener/event/EachRowAnalysisFinishEvent.java +++ b/src/main/java/com/alibaba/excel/read/listener/event/EachRowAnalysisFinishEvent.java @@ -1,6 +1,6 @@ package com.alibaba.excel.read.listener.event; -import java.util.List; +import java.util.Map; import com.alibaba.excel.metadata.CellData; @@ -8,13 +8,14 @@ import com.alibaba.excel.metadata.CellData; * @author jipengfei */ public class EachRowAnalysisFinishEvent implements AnalysisFinishEvent { - private List result; + private Map result; - public EachRowAnalysisFinishEvent(List content) { + public EachRowAnalysisFinishEvent(Map content) { this.result = content; } + @Override - public List getAnalysisResult() { + public Map getAnalysisResult() { return result; } } diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java index 48e92bb..b0391a6 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java @@ -107,9 +107,9 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH @Override public void notifyEndOneRow(AnalysisFinishEvent event, AnalysisContext analysisContext) { - List cellDataList = event.getAnalysisResult(); + Map cellDataMap = event.getAnalysisResult(); ReadRowHolder readRowHolder = analysisContext.readRowHolder(); - readRowHolder.setCurrentRowAnalysisResult(cellDataList); + readRowHolder.setCurrentRowAnalysisResult(cellDataMap); if (readRowHolder.getRowIndex() >= analysisContext.readSheetHolder().getHeadRowNumber()) { for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { @@ -129,7 +129,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH } // Now is header if (analysisContext.readSheetHolder().getHeadRowNumber().equals(readRowHolder.getRowIndex() + 1)) { - buildHead(analysisContext, cellDataList); + buildHead(analysisContext, cellDataMap); } } @@ -140,11 +140,11 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH } } - private void buildHead(AnalysisContext analysisContext, List cellDataList) { + private void buildHead(AnalysisContext analysisContext, Map cellDataMap) { if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { return; } - List dataList = (List)buildStringList(cellDataList, analysisContext.currentReadHolder()); + List dataList = (List)buildStringList(cellDataMap, analysisContext.currentReadHolder()); ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); Map headMapData = excelHeadPropertyData.getHeadMap(); Map contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); @@ -153,7 +153,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH new HashMap(contentPropertyMapData.size() * 4 / 3 + 1); for (Map.Entry entry : headMapData.entrySet()) { Head headData = entry.getValue(); - if (headData.getForceIndex()) { + if (headData.getForceIndex() || !headData.getForceName()) { tmpHeadMap.put(entry.getKey(), headData); tmpContentPropertyMap.put(entry.getKey(), contentPropertyMapData.get(entry.getKey())); continue; @@ -179,9 +179,9 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH excelHeadPropertyData.setContentPropertyMap(tmpContentPropertyMap); } - private Object buildStringList(List data, ReadHolder readHolder) { + private Object buildStringList(Map cellDataMa, ReadHolder readHolder) { List list = new ArrayList(); - for (CellData cellData : data) { + for (CellData cellData : cellDataMa.values()) { Converter converter = readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType())); if (converter == null) { diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index ec9ba52..fa028e5 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -256,25 +256,7 @@ public class ExcelBuilderImpl implements ExcelBuilder { if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { value = ((String)value).trim(); } - Converter converter = null; - if (excelContentProperty != null) { - converter = excelContentProperty.getConverter(); - } - if (converter == null) { - converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); - } - if (converter == null) { - throw new ExcelDataConvertException( - "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); - } - CellData cellData; - try { - cellData = - converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); - } catch (Exception e) { - throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), - e); - } + CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); if (cellData == null || cellData.getType() == null) { throw new ExcelDataConvertException( "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); @@ -297,4 +279,31 @@ public class ExcelBuilderImpl implements ExcelBuilder { + "at row:" + cell.getRow().getRowNum()); } } + + private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value instanceof CellData) { + return (CellData)value; + } + Converter converter = null; + if (excelContentProperty != null) { + converter = excelContentProperty.getConverter(); + } + if (converter == null) { + converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); + } + if (converter == null) { + throw new ExcelDataConvertException( + "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); + } + CellData cellData; + try { + cellData = + converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); + } catch (Exception e) { + throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), + e); + } + return cellData; + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java index 800ede0..0ecc343 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterData.java @@ -4,6 +4,7 @@ import java.math.BigDecimal; import java.util.Date; import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.metadata.CellData; import lombok.Data; @@ -32,4 +33,6 @@ public class ConverterData { private Float floatData; @ExcelProperty("字符串") private String string; + @ExcelProperty("自定义") + private CellData cellData; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java index 9992e61..d688c70 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java @@ -45,6 +45,7 @@ public class ConverterDataListener extends AnalysisEventListener Assert.assertEquals(data.getDoubleData(), 1.0, 0.0); Assert.assertEquals(data.getFloatData(), (float)1.0, 0.0); Assert.assertEquals(data.getString(), "测试"); + Assert.assertEquals(data.getCellData().getStringValue(), "自定义"); LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java index be13972..d97901b 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java @@ -5,12 +5,14 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcelFactory; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.util.DateUtils; /** @@ -21,8 +23,14 @@ import com.alibaba.excel.util.DateUtils; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ConverterDataTest { - private File file07 = TestFileUtil.createNewFile("converter07.xlsx"); - private File file03 = TestFileUtil.createNewFile("converter03.xls"); + private static File file07; + private static File file03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("converter07.xlsx"); + file03 = TestFileUtil.createNewFile("converter03.xls"); + } @Test public void T01ReadAndWrite07() throws Exception { @@ -39,6 +47,22 @@ public class ConverterDataTest { EasyExcelFactory.read(file, ConverterData.class, new ConverterDataListener()).sheet().doRead().finish(); } + @Test + public void T03ReadAllConverter07() { + readAllConverter("converter" + File.separator + "converter07.xlsx"); + } + + @Test + public void T03ReadAllConverter03() { + readAllConverter("converter" + File.separator + "converter03.xls"); + } + + private void readAllConverter(String fileName) { + EasyExcelFactory + .read(TestFileUtil.readFile(fileName), ReadAllConverterData.class, new ReadAllConverterDataListener()) + .sheet().doRead().finish(); + } + private List data() throws Exception { List list = new ArrayList(); ConverterData converterData = new ConverterData(); @@ -52,6 +76,7 @@ public class ConverterDataTest { converterData.setDoubleData(1.0); converterData.setFloatData((float)1.0); converterData.setString("测试"); + converterData.setCellData(new CellData("自定义")); list.add(converterData); return list; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java new file mode 100644 index 0000000..19e4b28 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java @@ -0,0 +1,45 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.util.Date; + +import lombok.Data; + +/** + * @author zhuangjiaju + */ +@Data +public class ReadAllConverterData { + private BigDecimal bigDecimalBoolean; + private BigDecimal bigDecimalNumber; + private BigDecimal bigDecimalString; + private Boolean booleanBoolean; + private Boolean booleanNumber; + private Boolean booleanString; + private Byte byteBoolean; + private Byte byteNumber; + private Byte byteString; + private Date dateNumber; + private Date dateString; + private Double doubleBoolean; + private Double doubleNumber; + private Double doubleString; + private Float floatBoolean; + private Float floatNumber; + private Float floatString; + private Integer integerBoolean; + private Integer integerNumber; + private Integer integerString; + private Long longBoolean; + private Long longNumber; + private Long longString; + private Short shortBoolean; + private Short shortNumber; + private Short shortString; + private String StringBoolean; + private String StringNumber; + private String StringString; + private String StringError; + private String StringFormulaNumber; + private String StringFormulaString; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java new file mode 100644 index 0000000..6335246 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java @@ -0,0 +1,46 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSON; + +/** + * @author zhuangjiaju + */ +public class ReadAllConverterDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ReadAllConverterDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(ReadAllConverterData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assert.assertEquals(list.size(), 1); + ReadAllConverterData data = list.get(0); + // try { + // Assert.assertEquals(data.getDate(), DateUtils.parseDate("2020-01-01 01:01:01")); + // } catch (ParseException e) { + // throw new ExcelCommonException("Test Exception", e); + // } + // Assert.assertEquals(data.getBooleanData(), Boolean.TRUE); + // Assert.assertEquals(data.getBigDecimal().doubleValue(), BigDecimal.ONE.doubleValue(), 0.0); + // Assert.assertEquals((long)data.getLongData(), 1L); + // Assert.assertEquals((long)data.getIntegerData(), 1L); + // Assert.assertEquals((long)data.getShortData(), 1L); + // Assert.assertEquals((long)data.getByteData(), 1L); + // Assert.assertEquals(data.getDoubleData(), 1.0, 0.0); + // Assert.assertEquals(data.getFloatData(), (float)1.0, 0.0); + // Assert.assertEquals(data.getString(), "测试"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadConverterData.java b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadConverterData.java deleted file mode 100644 index 35f53e1..0000000 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadConverterData.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.alibaba.easyexcel.test.core.converter; - -import java.math.BigDecimal; -import java.util.Date; - -import com.alibaba.excel.annotation.ExcelProperty; - -import lombok.Data; - -/** - * @author zhuangjiaju - */ -@Data -public class ReadConverterData { - @ExcelProperty("日期") - private Date date; - @ExcelProperty("日期字符串") - private String dateString; - @ExcelProperty("布尔") - private Boolean booleanData; - @ExcelProperty("布尔字符串") - private String booleanString; - @ExcelProperty("大数") - private BigDecimal bigDecimal; - @ExcelProperty("大数字符串") - private String bigDecimalString; - @ExcelProperty("长整型") - private Long longData; - @ExcelProperty("长整型字符串") - private String longString; - @ExcelProperty("整型") - private Integer integerData; - @ExcelProperty("整型字符串") - private String integerString; - @ExcelProperty("短整型") - private Short shortData; - @ExcelProperty("短整型字符串") - private String shortString; - @ExcelProperty("字节型") - private Byte byteData; - @ExcelProperty("字节型字符串") - private String byteString; - @ExcelProperty("双精度浮点型") - private Double doulbleData; - @ExcelProperty("双精度浮点型字符串") - private String doulbleString; - @ExcelProperty("浮点型") - private Float FloatData; - @ExcelProperty("浮点型字符串") - private String FloatString; - @ExcelProperty("字符串") - private String string; -} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/nohead/NoHeadData07Test.java b/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData07Test.java similarity index 95% rename from src/test/java/com/alibaba/easyexcel/test/core/nohead/NoHeadData07Test.java rename to src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData07Test.java index 4d0af58..1eae01f 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/nohead/NoHeadData07Test.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData07Test.java @@ -1,4 +1,4 @@ -package com.alibaba.easyexcel.test.core.nohead; +package com.alibaba.easyexcel.test.core.head; import java.util.ArrayList; import java.util.List; @@ -9,7 +9,7 @@ import com.alibaba.easyexcel.test.core.order.OrderData; /** * Order data test - * + * * @author zhuangjiaju */ public class NoHeadData07Test { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java index 7058204..1170483 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; @@ -20,11 +21,17 @@ import com.alibaba.excel.EasyExcelFactory; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SimpleDataTest { - private File file07 = TestFileUtil.createNewFile("simple07.xlsx"); - private File file03 = TestFileUtil.createNewFile("simple03.xls"); + private static File file07; + private static File file03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("simple07.xlsx"); + file03 = TestFileUtil.createNewFile("simple03.xls"); + } @Test - public void T01ReadAndWrite() { + public void T01ReadAndWrite07() { readAndWrite(file07); } @@ -48,8 +55,6 @@ public class SimpleDataTest { synchronousRead(file03); } - - private void synchronousRead(File file) { // Synchronous read file List list = EasyExcelFactory.read(file).head(SimpleData.class).sheet().doReadSync(); diff --git a/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java b/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java index 2c14e54..e43bdde 100644 --- a/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java +++ b/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java @@ -13,6 +13,10 @@ public class TestFileUtil { return TestFileUtil.class.getResource("/").getPath(); } + public static File readFile(String pathName) { + return new File(getPath() + pathName); + } + public static File createNewFile(String pathName) { File file = new File(getPath() + pathName); if (file.exists()) { @@ -25,7 +29,4 @@ public class TestFileUtil { return file; } - public static File createNewWriteFile(String pathName) { - return createNewFile("wirte/" + pathName); - } } diff --git a/src/test/resources/converter/converter03.xls b/src/test/resources/converter/converter03.xls new file mode 100644 index 0000000000000000000000000000000000000000..6ae32b9a24c20306a248bcc00380caadbb9ae717 GIT binary patch literal 20480 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