diff --git a/.travis.yml b/.travis.yml index 8191cf3..74750ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ cache: before_install: - chmod +x mvnw install: - - ./mvnw install -Dgpg.skip -B -V -Dmaven.test.skip=true + - ./mvnw install -B -V -Dmaven.test.skip=true - ./mvnw javadoc:javadoc \ No newline at end of file diff --git a/README.md b/README.md index b142b9d..ecd2000 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都 * [快速使用](https://alibaba-easyexcel.github.io/) * [关于软件](/abouteasyexcel.md) * [更新记事](/update.md) +* [贡献代码](https://alibaba-easyexcel.github.io/support/contribute.html) ## 维护者 玉霄、庄家钜、怀宇 @@ -31,7 +32,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja /** * 最简单的读 *

1. 创建excel对应的实体对象 参照{@link DemoData} - *

2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

3. 直接读即可 */ @Test @@ -82,7 +83,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja /** * 文件上传 *

1. 创建excel对应的实体对象 参照{@link UploadData} - *

2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} + *

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} *

3. 直接读即可 */ @PostMapping("upload") diff --git a/pom.xml b/pom.xml index fe46977..4a47b3d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.1.6 + 2.2.0-beta1 jar easyexcel @@ -15,9 +15,10 @@ - UTF-8 - 1.7 + 1.6 + true + true @@ -66,6 +67,11 @@ poi-ooxml 3.17 + + org.apache.poi + poi-ooxml-schemas + 3.17 + cglib cglib @@ -79,7 +85,7 @@ org.ehcache ehcache - 3.7.1 + 3.4.0 @@ -154,6 +160,8 @@ com/alibaba/excel/event/AnalysisEventListener.java + com/alibaba/excel/metadata/DataFormatter.java + com/alibaba/excel/util/DateUtils.java @@ -196,7 +204,6 @@ - org.apache.maven.plugins maven-gpg-plugin diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index eb4f188..5487ee1 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -14,15 +14,22 @@ import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.v03.XlsSaxAnalyser; import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.context.xls.DefaultXlsReadContext; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.context.xlsx.DefaultXlsxReadContext; +import com.alibaba.excel.context.xlsx.XlsxReadContext; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelAnalysisStopException; import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; import com.alibaba.excel.util.StringUtils; /** @@ -41,8 +48,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { try { - analysisContext = new AnalysisContextImpl(readWorkbook); - choiceExcelExecutor(); + choiceExcelExecutor(readWorkbook); } catch (RuntimeException e) { finish(); throw e; @@ -52,29 +58,25 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } } - private void choiceExcelExecutor() throws Exception { - ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); - ExcelTypeEnum excelType = readWorkbookHolder.getExcelType(); - if (excelType == null) { - excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); - return; - } + private void choiceExcelExecutor(ReadWorkbook readWorkbook) throws Exception { + ExcelTypeEnum excelType = ExcelTypeEnum.valueOf(readWorkbook); switch (excelType) { case XLS: POIFSFileSystem poifsFileSystem; - if (readWorkbookHolder.getFile() != null) { - poifsFileSystem = new POIFSFileSystem(readWorkbookHolder.getFile()); + if (readWorkbook.getFile() != null) { + poifsFileSystem = new POIFSFileSystem(readWorkbook.getFile()); } else { - poifsFileSystem = new POIFSFileSystem(readWorkbookHolder.getInputStream()); + poifsFileSystem = new POIFSFileSystem(readWorkbook.getInputStream()); } // So in encrypted excel, it looks like XLS but it's actually XLSX if (poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) { InputStream decryptedStream = null; try { - decryptedStream = - DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), - analysisContext.readWorkbookHolder().getPassword()); - excelReadExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); + decryptedStream = DocumentFactoryHelper + .getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), readWorkbook.getPassword()); + XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX); + analysisContext = xlsxReadContext; + excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, decryptedStream); return; } finally { IOUtils.closeQuietly(decryptedStream); @@ -83,15 +85,21 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { poifsFileSystem.close(); } } - if (analysisContext.readWorkbookHolder().getPassword() != null) { - Biff8EncryptionKey.setCurrentUserPassword(analysisContext.readWorkbookHolder().getPassword()); + if (readWorkbook.getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(readWorkbook.getPassword()); } - excelReadExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); + XlsReadContext xlsReadContext = new DefaultXlsReadContext(readWorkbook, ExcelTypeEnum.XLS); + xlsReadContext.xlsReadWorkbookHolder().setPoifsFileSystem(poifsFileSystem); + analysisContext = xlsReadContext; + excelReadExecutor = new XlsSaxAnalyser(xlsReadContext); break; case XLSX: - excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); + XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX); + analysisContext = xlsxReadContext; + excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, null); break; default: + break; } } @@ -101,19 +109,15 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { if (!readAll && CollectionUtils.isEmpty(readSheetList)) { throw new IllegalArgumentException("Specify at least one read sheet."); } + analysisContext.readWorkbookHolder().setParameterSheetDataList(readSheetList); + analysisContext.readWorkbookHolder().setReadAll(readAll); try { - excelReadExecutor.execute(readSheetList, readAll); + excelReadExecutor.execute(); } catch (ExcelAnalysisStopException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Custom stop!"); } } - // The last sheet is read - if (excelReadExecutor instanceof XlsSaxAnalyser) { - if (analysisContext.readSheetHolder() != null) { - analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); - } - } } catch (RuntimeException e) { finish(); throw e; @@ -144,15 +148,17 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { throwable = t; } try { - if (readWorkbookHolder.getOpcPackage() != null) { - readWorkbookHolder.getOpcPackage().revert(); + if ((readWorkbookHolder instanceof XlsxReadWorkbookHolder) + && ((XlsxReadWorkbookHolder)readWorkbookHolder).getOpcPackage() != null) { + ((XlsxReadWorkbookHolder)readWorkbookHolder).getOpcPackage().revert(); } } catch (Throwable t) { throwable = t; } try { - if (readWorkbookHolder.getPoifsFileSystem() != null) { - readWorkbookHolder.getPoifsFileSystem().close(); + if ((readWorkbookHolder instanceof XlsReadWorkbookHolder) + && ((XlsReadWorkbookHolder)readWorkbookHolder).getPoifsFileSystem() != null) { + ((XlsReadWorkbookHolder)readWorkbookHolder).getPoifsFileSystem().close(); } } catch (Throwable t) { throwable = t; @@ -175,11 +181,18 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { clearEncrypt03(); + removeThreadLocalCache(); + if (throwable != null) { throw new ExcelAnalysisException("Can not close IO.", throwable); } } + private void removeThreadLocalCache() { + NumberDataFormatterUtils.removeThreadLocalCache(); + DateUtils.removeThreadLocalCache(); + } + private void clearEncrypt03() { if (StringUtils.isEmpty(analysisContext.readWorkbookHolder().getPassword()) || !ExcelTypeEnum.XLS.equals(analysisContext.readWorkbookHolder().getExcelType())) { diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java index 10ced98..085caab 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -20,11 +20,6 @@ public interface ExcelReadExecutor { /** * Read the sheet. - * - * @param readSheetList - * Which sheets you need to read. - * @param readAll - * The readSheetList parameter is ignored, and all sheets are read. */ - void execute(List readSheetList, Boolean readAll); + void execute(); } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/AbstractXlsRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/AbstractXlsRecordHandler.java deleted file mode 100644 index 6128708..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v03/AbstractXlsRecordHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.excel.analysis.v03; - -import com.alibaba.excel.metadata.CellData; - -/** - * - * @author Dan Zheng - */ -public abstract class AbstractXlsRecordHandler implements XlsRecordHandler { - protected int row = -1; - protected int column = -1; - protected CellData cellData; - - @Override - public int getRow() { - return row; - } - - @Override - public int getColumn() { - return column; - } - - @Override - public CellData getCellData() { - return cellData; - } - - @Override - public int compareTo(XlsRecordHandler o) { - return this.getOrder() - o.getOrder(); - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java new file mode 100644 index 0000000..cb7e4a8 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java @@ -0,0 +1,8 @@ +package com.alibaba.excel.analysis.v03; + +/** + * Need to ignore the current handler without reading the current sheet. + * + * @author Jiaju Zhuang + */ +public interface IgnorableXlsRecordHandler extends XlsRecordHandler {} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java index 6f8e716..cfdc6be 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java @@ -1,8 +1,8 @@ package com.alibaba.excel.analysis.v03; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder; import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; @@ -10,13 +10,14 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; import org.apache.poi.hssf.eventusermodel.HSSFListener; import org.apache.poi.hssf.eventusermodel.HSSFRequest; import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; import org.apache.poi.hssf.record.Record; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; -import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.analysis.v03.handlers.BoundSheetRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.read.metadata.ReadSheet; /** * In some cases, you need to know the number of sheets in advance and only read the file once in advance. @@ -24,24 +25,29 @@ import com.alibaba.excel.read.metadata.ReadSheet; * @author Jiaju Zhuang */ public class XlsListSheetListener implements HSSFListener { - private POIFSFileSystem poifsFileSystem; - private List sheetList; - private BofRecordHandler bofRecordHandler; - - public XlsListSheetListener(AnalysisContext analysisContext, POIFSFileSystem poifsFileSystem) { - this.poifsFileSystem = poifsFileSystem; - sheetList = new ArrayList(); - bofRecordHandler = new BofRecordHandler(analysisContext, sheetList, false, false); - bofRecordHandler.init(); - bofRecordHandler.init(null, true); + private XlsReadContext xlsReadContext; + private static final Map XLS_RECORD_HANDLER_MAP = new HashMap(); + + static { + XLS_RECORD_HANDLER_MAP.put(BOFRecord.sid, new BofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoundSheetRecord.sid, new BoundSheetRecordHandler()); + } + + public XlsListSheetListener(XlsReadContext xlsReadContext) { + this.xlsReadContext = xlsReadContext; + xlsReadContext.xlsReadWorkbookHolder().setNeedReadSheet(Boolean.FALSE); } @Override public void processRecord(Record record) { - bofRecordHandler.processRecord(record); + XlsRecordHandler handler = XLS_RECORD_HANDLER_MAP.get(record.getSid()); + if (handler == null) { + return; + } + handler.processRecord(xlsReadContext, record); } - public List getSheetList() { + public void execute() { MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); HSSFListener formatListener = new FormatTrackingHSSFListener(listener); HSSFEventFactory factory = new HSSFEventFactory(); @@ -49,12 +55,10 @@ public class XlsListSheetListener implements HSSFListener { EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); request.addListenerForAllRecords(workbookBuildingListener); - try { - factory.processWorkbookEvents(request, poifsFileSystem); + factory.processWorkbookEvents(request, xlsReadContext.xlsReadWorkbookHolder().getPoifsFileSystem()); } catch (IOException e) { throw new ExcelAnalysisException(e); } - return sheetList; } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java index b786bcb..ca9218a 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java @@ -2,60 +2,28 @@ package com.alibaba.excel.analysis.v03; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.context.xls.XlsReadContext; /** * Intercepts handle xls reads. * * @author Dan Zheng */ -public interface XlsRecordHandler extends Comparable { +public interface XlsRecordHandler { /** - * Which tags are supported + * Whether to support * + * @param xlsReadContext * @param record - * Excel analysis record - * @return Which tags are supported + * @return */ - boolean support(Record record); - - /** - * Initialize - */ - void init(); + boolean support(XlsReadContext xlsReadContext, Record record); /** * Processing record * + * @param xlsReadContext * @param record */ - void processRecord(Record record); - - /** - * Get row - * - * @return Row index - */ - int getRow(); - - /** - * Get column - * - * @return Column index - */ - int getColumn(); - - /** - * Get value - * - * @return Excel internal cell data - */ - CellData getCellData(); - - /** - * Get order - * - * @return Order - */ - int getOrder(); + void processRecord(XlsReadContext xlsReadContext, Record record); } 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 e0c150c..2b92253 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -1,12 +1,9 @@ package com.alibaba.excel.analysis.v03; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.HashMap; 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; @@ -16,32 +13,52 @@ import org.apache.poi.hssf.eventusermodel.HSSFRequest; import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BlankRecord; +import org.apache.poi.hssf.record.BoolErrRecord; import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.HyperlinkRecord; +import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.LabelRecord; +import org.apache.poi.hssf.record.LabelSSTRecord; +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.NoteRecord; +import org.apache.poi.hssf.record.NumberRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.RKRecord; import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.hssf.record.SSTRecord; +import org.apache.poi.hssf.record.StringRecord; +import org.apache.poi.hssf.record.TextObjectRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.ExcelReadExecutor; -import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BlankRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BoolErrRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BoundSheetRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.DummyRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.EofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.HyperlinkRecordHandler; import com.alibaba.excel.analysis.v03.handlers.IndexRecordHandler; import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler; -import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.LabelSstRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.MergeCellsRecordHandler; import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler; import com.alibaba.excel.analysis.v03.handlers.NumberRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.ObjRecordHandler; import com.alibaba.excel.analysis.v03.handlers.RkRecordHandler; import com.alibaba.excel.analysis.v03.handlers.SstRecordHandler; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.analysis.v03.handlers.StringRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.TextObjectRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.read.listener.event.EachRowAnalysisFinishEvent; +import com.alibaba.excel.exception.ExcelAnalysisStopException; import com.alibaba.excel.read.metadata.ReadSheet; -import com.alibaba.excel.read.metadata.holder.ReadRowHolder; -import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; /** * /** * A text extractor for Excel files. * @@ -60,168 +77,90 @@ import com.alibaba.excel.util.CollectionUtils; */ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); + private static final short DUMMY_RECORD_SID = -1; + private XlsReadContext xlsReadContext; + private static final Map XLS_RECORD_HANDLER_MAP = new HashMap(32); + + static { + XLS_RECORD_HANDLER_MAP.put(BlankRecord.sid, new BlankRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BOFRecord.sid, new BofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoolErrRecord.sid, new BoolErrRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoundSheetRecord.sid, new BoundSheetRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(DUMMY_RECORD_SID, new DummyRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(EOFRecord.sid, new EofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(FormulaRecord.sid, new FormulaRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(HyperlinkRecord.sid, new HyperlinkRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(IndexRecord.sid, new IndexRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(LabelRecord.sid, new LabelRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(LabelSSTRecord.sid, new LabelSstRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(MergeCellsRecord.sid, new MergeCellsRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(NoteRecord.sid, new NoteRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(NumberRecord.sid, new NumberRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(ObjRecord.sid, new ObjRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(RKRecord.sid, new RkRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(SSTRecord.sid, new SstRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(StringRecord.sid, new StringRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(TextObjectRecord.sid, new TextObjectRecordHandler()); + } - private POIFSFileSystem poifsFileSystem; - private Boolean readAll; - private List readSheetList; - private int lastRowNumber; - private int lastColumnNumber; - /** - * For parsing Formulas - */ - private EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener; - private FormatTrackingHSSFListener formatListener; - private Map records; - private List sheets; - private HSSFWorkbook stubWorkbook; - private List recordHandlers = new ArrayList(); - private AnalysisContext analysisContext; - - public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) { - this.analysisContext = context; - this.records = new LinkedHashMap(); - this.poifsFileSystem = poifsFileSystem; - analysisContext.readWorkbookHolder().setPoifsFileSystem(poifsFileSystem); + public XlsSaxAnalyser(XlsReadContext xlsReadContext) { + this.xlsReadContext = xlsReadContext; } @Override public List sheetList() { - if (sheets == null) { - LOGGER.warn("Getting the 'sheetList' before reading will cause the file to be read twice."); - XlsListSheetListener xlsListSheetListener = new XlsListSheetListener(analysisContext, poifsFileSystem); - sheets = xlsListSheetListener.getSheetList(); + try { + if (xlsReadContext.readWorkbookHolder().getActualSheetDataList() == null) { + new XlsListSheetListener(xlsReadContext).execute(); + } + } catch (ExcelAnalysisStopException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Custom stop!"); + } } - return sheets; + return xlsReadContext.readWorkbookHolder().getActualSheetDataList(); } @Override - public void execute(List readSheetList, Boolean readAll) { - this.readAll = readAll; - this.readSheetList = readSheetList; + public void execute() { + XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); - formatListener = new FormatTrackingHSSFListener(listener); - workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); - if (workbookBuildingListener != null && stubWorkbook == null) { - stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook(); - } - init(); + xlsReadWorkbookHolder.setFormatTrackingHSSFListener(new FormatTrackingHSSFListener(listener)); + EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener = + new EventWorkbookBuilder.SheetRecordCollectingListener( + xlsReadWorkbookHolder.getFormatTrackingHSSFListener()); + xlsReadWorkbookHolder.setHssfWorkbook(workbookBuildingListener.getStubHSSFWorkbook()); HSSFEventFactory factory = new HSSFEventFactory(); HSSFRequest request = new HSSFRequest(); - request.addListenerForAllRecords(formatListener); + request.addListenerForAllRecords(xlsReadWorkbookHolder.getFormatTrackingHSSFListener()); try { - factory.processWorkbookEvents(request, poifsFileSystem); + factory.processWorkbookEvents(request, xlsReadWorkbookHolder.getPoifsFileSystem()); } catch (IOException e) { throw new ExcelAnalysisException(e); } // Sometimes tables lack the end record of the last column - if (!records.isEmpty()) { - endRow(); + if (!xlsReadContext.xlsReadSheetHolder().getCellMap().isEmpty()) { + // Forge a termination data + processRecord(new LastCellOfRowDummyRecord(xlsReadContext.xlsReadSheetHolder().getRowIndex() + 1, -1)); } } - private void init() { - lastRowNumber = 0; - lastColumnNumber = 0; - records = new LinkedHashMap(); - buildXlsRecordHandlers(); - } - @Override public void processRecord(Record record) { - // Not data from the current sheet - if (ignoreRecord(record)) { + XlsRecordHandler handler = XLS_RECORD_HANDLER_MAP.get(record.getSid()); + if (handler == null) { return; } - int thisRow = -1; - int thisColumn = -1; - CellData cellData = null; - for (XlsRecordHandler handler : this.recordHandlers) { - if (handler.support(record)) { - handler.processRecord(record); - thisRow = handler.getRow(); - thisColumn = handler.getColumn(); - cellData = handler.getCellData(); - if (cellData != null) { - cellData.checkEmpty(); - if (CellDataTypeEnum.EMPTY != cellData.getType()) { - records.put(thisColumn, cellData); - } - } - break; - } - } - // If we got something to print out, do so - if (cellData != null && analysisContext.currentReadHolder().globalConfiguration().getAutoTrim() - && CellDataTypeEnum.STRING == cellData.getType()) { - cellData.setStringValue(cellData.getStringValue().trim()); - } - - // Handle new row - if (thisRow != -1 && thisRow != lastRowNumber) { - lastColumnNumber = -1; - } - - // Update column and row count - if (thisRow > -1) { - lastRowNumber = thisRow; - } - if (thisColumn > -1) { - lastColumnNumber = thisColumn; - } - - processLastCellOfRow(record); - } - - private boolean ignoreRecord(Record record) { - return analysisContext.readWorkbookHolder().getIgnoreRecord03() && record.getSid() != BoundSheetRecord.sid - && record.getSid() != BOFRecord.sid; - } - - private void processLastCellOfRow(Record record) { - // Handle end of row - if (record instanceof LastCellOfRowDummyRecord) { - endRow(); + boolean ignoreRecord = (handler instanceof IgnorableXlsRecordHandler) + && xlsReadContext.xlsReadSheetHolder() != null && xlsReadContext.xlsReadSheetHolder().getIgnoreRecord(); + if (ignoreRecord) { + // No need to read the current sheet + return; } - } - - private void endRow() { - if (lastColumnNumber == -1) { - lastColumnNumber = 0; + if (!handler.support(xlsReadContext, record)) { + return; } - analysisContext.readRowHolder( - new ReadRowHolder(lastRowNumber, analysisContext.readSheetHolder().getGlobalConfiguration())); - analysisContext.readSheetHolder().notifyEndOneRow(new EachRowAnalysisFinishEvent(records), analysisContext); - records.clear(); - lastColumnNumber = -1; + handler.processRecord(xlsReadContext, record); } - private void buildXlsRecordHandlers() { - if (CollectionUtils.isEmpty(recordHandlers)) { - recordHandlers.add(new BlankOrErrorRecordHandler()); - // The table has been counted and there are no duplicate statistics - if (sheets == null) { - sheets = new ArrayList(); - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, false, true)); - } else { - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, true, true)); - } - recordHandlers.add(new FormulaRecordHandler(stubWorkbook, formatListener)); - recordHandlers.add(new LabelRecordHandler()); - recordHandlers.add(new NoteRecordHandler()); - recordHandlers.add(new NumberRecordHandler(formatListener)); - recordHandlers.add(new RkRecordHandler()); - recordHandlers.add(new SstRecordHandler()); - recordHandlers.add(new MissingCellDummyRecordHandler()); - recordHandlers.add(new IndexRecordHandler(analysisContext)); - Collections.sort(recordHandlers); - } - - for (XlsRecordHandler x : recordHandlers) { - x.init(); - if (x instanceof BofRecordHandler) { - BofRecordHandler bofRecordHandler = (BofRecordHandler)x; - bofRecordHandler.init(readSheetList, readAll); - } - } - } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java new file mode 100644 index 0000000..93b3d1a --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java @@ -0,0 +1,19 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.XlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Abstract xls record handler + * + * @author Jiaju Zhuang + **/ +public abstract class AbstractXlsRecordHandler implements XlsRecordHandler { + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return true; + } +} 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 deleted file mode 100644 index f64d437..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankOrErrorRecordHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.alibaba.excel.analysis.v03.handlers; - -import org.apache.poi.hssf.record.BlankRecord; -import org.apache.poi.hssf.record.BoolErrRecord; -import org.apache.poi.hssf.record.Record; - -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; - -/** - * Record handler - * - * @author Dan Zheng - */ -public class BlankOrErrorRecordHandler extends AbstractXlsRecordHandler { - - @Override - public boolean support(Record record) { - return BlankRecord.sid == record.getSid() || BoolErrRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - if (record.getSid() == BlankRecord.sid) { - BlankRecord br = (BlankRecord)record; - this.row = br.getRow(); - this.column = br.getColumn(); - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - } else if (record.getSid() == BoolErrRecord.sid) { - BoolErrRecord ber = (BoolErrRecord)record; - this.row = ber.getRow(); - this.column = ber.getColumn(); - this.cellData = new CellData(ber.getBooleanValue()); - } - } - - @Override - public void init() { - - } - - @Override - public int getOrder() { - return 0; - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java new file mode 100644 index 0000000..e6290eb --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.BlankRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.metadata.CellData; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BlankRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BlankRecord br = (BlankRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)br.getColumn(), + CellData.newEmptyInstance(br.getRow(), (int)br.getColumn())); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java index 083edbc..66e3ab7 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java @@ -7,9 +7,10 @@ import org.apache.poi.hssf.record.BOFRecord; import org.apache.poi.hssf.record.BoundSheetRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.exception.ExcelAnalysisStopException; import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; import com.alibaba.excel.util.SheetUtils; /** @@ -18,91 +19,55 @@ import com.alibaba.excel.util.SheetUtils; * @author Dan Zheng */ public class BofRecordHandler extends AbstractXlsRecordHandler { - private List boundSheetRecords = new ArrayList(); - private BoundSheetRecord[] orderedBsrs; - private int sheetIndex; - private List sheets; - private Boolean readAll; - private List readSheetList; - private AnalysisContext context; - private boolean alreadyInit; - private boolean needInitSheet; - - public BofRecordHandler(AnalysisContext context, List sheets, boolean alreadyInit, - boolean needInitSheet) { - this.context = context; - this.sheets = sheets; - this.alreadyInit = alreadyInit; - this.needInitSheet = needInitSheet; - } - - @Override - public boolean support(Record record) { - return BoundSheetRecord.sid == record.getSid() || BOFRecord.sid == record.getSid(); - } @Override - public void processRecord(Record record) { - if (record.getSid() == BoundSheetRecord.sid) { - boundSheetRecords.add((BoundSheetRecord)record); - } else if (record.getSid() == BOFRecord.sid) { - BOFRecord br = (BOFRecord)record; - if (br.getType() == BOFRecord.TYPE_WORKSHEET) { - if (orderedBsrs == null) { - orderedBsrs = BoundSheetRecord.orderByBofPosition(boundSheetRecords); - } - String sheetName = orderedBsrs[sheetIndex].getSheetname(); - // Find the currently read sheet - ReadSheet readSheet = null; - if (!alreadyInit) { - readSheet = new ReadSheet(sheetIndex, sheetName); - sheets.add(readSheet); - } - if (needInitSheet) { - if (readSheet == null) { - for (ReadSheet sheet : sheets) { - if (sheet.getSheetNo() == sheetIndex) { - readSheet = sheet; - break; - } - } - } - assert readSheet != null : "Can't find the sheet."; - context.readWorkbookHolder().setIgnoreRecord03(Boolean.TRUE); - // Copy the parameter to the current sheet - readSheet = SheetUtils.match(readSheet, readSheetList, readAll, - context.readWorkbookHolder().getGlobalConfiguration()); - if (readSheet != null) { - if (readSheet.getSheetNo() != 0 && context.readSheetHolder() != null) { - // Prompt for the end of the previous form read - context.readSheetHolder().notifyAfterAllAnalysed(context); - } - context.currentSheet(readSheet); - context.readWorkbookHolder().setIgnoreRecord03(Boolean.FALSE); - } - } - sheetIndex++; - } + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BOFRecord br = (BOFRecord)record; + XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); + if (br.getType() == BOFRecord.TYPE_WORKBOOK) { + xlsReadWorkbookHolder.setReadSheetIndex(null); + return; } - } - - @Override - public void init() { - sheetIndex = 0; - orderedBsrs = null; - boundSheetRecords.clear(); - if (!alreadyInit) { - sheets.clear(); + if (br.getType() != BOFRecord.TYPE_WORKSHEET) { + return; } + // Init read sheet Data + initReadSheetDataList(xlsReadWorkbookHolder); + Integer readSheetIndex = xlsReadWorkbookHolder.getReadSheetIndex(); + if (readSheetIndex == null) { + readSheetIndex = 0; + xlsReadWorkbookHolder.setReadSheetIndex(readSheetIndex); + } + ReadSheet readSheet = xlsReadWorkbookHolder.getActualSheetDataList().get(readSheetIndex); + assert readSheet != null : "Can't find the sheet."; + // Copy the parameter to the current sheet + readSheet = SheetUtils.match(readSheet, xlsReadContext); + if (readSheet != null) { + xlsReadContext.currentSheet(readSheet); + xlsReadContext.xlsReadSheetHolder().setIgnoreRecord(Boolean.FALSE); + } else { + xlsReadContext.xlsReadSheetHolder().setIgnoreRecord(Boolean.TRUE); + } + // Go read the next one + xlsReadWorkbookHolder.setReadSheetIndex(xlsReadWorkbookHolder.getReadSheetIndex() + 1); } - public void init(List readSheetList, Boolean readAll) { - this.readSheetList = readSheetList; - this.readAll = readAll; - } - - @Override - public int getOrder() { - return 0; + private void initReadSheetDataList(XlsReadWorkbookHolder xlsReadWorkbookHolder) { + if (xlsReadWorkbookHolder.getActualSheetDataList() != null) { + return; + } + BoundSheetRecord[] boundSheetRecords = + BoundSheetRecord.orderByBofPosition(xlsReadWorkbookHolder.getBoundSheetRecordList()); + List readSheetDataList = new ArrayList(); + for (int i = 0; i < boundSheetRecords.length; i++) { + BoundSheetRecord boundSheetRecord = boundSheetRecords[i]; + ReadSheet readSheet = new ReadSheet(i, boundSheetRecord.getSheetname()); + readSheetDataList.add(readSheet); + } + xlsReadWorkbookHolder.setActualSheetDataList(readSheetDataList); + // Just need to get the list of sheets + if (!xlsReadWorkbookHolder.getNeedReadSheet()) { + throw new ExcelAnalysisStopException("Just need to get the list of sheets."); + } } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java new file mode 100644 index 0000000..8d43521 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java @@ -0,0 +1,25 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.BoolErrRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.CellData; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BoolErrRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BoolErrRecord ber = (BoolErrRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)ber.getColumn(), + CellData.newInstance(ber.getBooleanValue(), ber.getRow(), (int)ber.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java new file mode 100644 index 0000000..685dcc6 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BoundSheetRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BoundSheetRecord bsr = (BoundSheetRecord)record; + xlsReadContext.xlsReadWorkbookHolder().getBoundSheetRecordList().add((BoundSheetRecord)record); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java new file mode 100644 index 0000000..54a2d1d --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.LinkedHashMap; + +import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; +import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class DummyRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + if (record instanceof LastCellOfRowDummyRecord) { + // End of this row + LastCellOfRowDummyRecord lcrdr = (LastCellOfRowDummyRecord)record; + xlsReadSheetHolder.setRowIndex(lcrdr.getRow()); + xlsReadContext.readRowHolder(new ReadRowHolder(lcrdr.getRow(), xlsReadSheetHolder.getTempRowType(), + xlsReadContext.readSheetHolder().getGlobalConfiguration(), xlsReadSheetHolder.getCellMap())); + xlsReadContext.analysisEventProcessor().endRow(xlsReadContext); + xlsReadSheetHolder.setCellMap(new LinkedHashMap()); + xlsReadSheetHolder.setTempRowType(RowTypeEnum.EMPTY); + } else if (record instanceof MissingCellDummyRecord) { + MissingCellDummyRecord mcdr = (MissingCellDummyRecord)record; + xlsReadSheetHolder.getCellMap().put(mcdr.getColumn(), + CellData.newEmptyInstance(mcdr.getRow(), mcdr.getColumn())); + } + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java new file mode 100644 index 0000000..0ca7313 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class EofRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + if (xlsReadContext.readSheetHolder() != null) { + xlsReadContext.analysisEventProcessor().endSheet(xlsReadContext); + } + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java index 0ff8f99..099b308 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java @@ -1,19 +1,20 @@ package com.alibaba.excel.analysis.v03.handlers; import java.math.BigDecimal; +import java.util.Map; -import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.record.FormulaRecord; import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.StringRecord; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.CellType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.Cell; import com.alibaba.excel.metadata.CellData; /** @@ -21,96 +22,59 @@ import com.alibaba.excel.metadata.CellData; * * @author Dan Zheng */ -public class FormulaRecordHandler extends AbstractXlsRecordHandler { +public class FormulaRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { private static final Logger LOGGER = LoggerFactory.getLogger(FormulaRecordHandler.class); - private static final String ERROR = "#VALUE!"; - private int nextRow; - private int nextColumn; - private boolean outputNextStringRecord; - private CellData tempCellData; - private FormatTrackingHSSFListener formatListener; - private HSSFWorkbook stubWorkbook; - - public FormulaRecordHandler(HSSFWorkbook stubWorkbook, FormatTrackingHSSFListener formatListener) { - this.stubWorkbook = stubWorkbook; - this.formatListener = formatListener; - } @Override - public boolean support(Record record) { - return FormulaRecord.sid == record.getSid() || StringRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - if (record.getSid() == FormulaRecord.sid) { - FormulaRecord frec = (FormulaRecord)record; - - this.row = frec.getRow(); - this.column = frec.getColumn(); - CellType cellType = CellType.forInt(frec.getCachedResultType()); - String formulaValue = null; - try { - formulaValue = HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()); - } catch (Exception e) { - LOGGER.warn("Get formula value error.{}", e.getMessage()); - } - switch (cellType) { - case STRING: - // Formula result is a string - // This is stored in the next record - outputNextStringRecord = true; - nextRow = frec.getRow(); - nextColumn = frec.getColumn(); - tempCellData = new CellData(CellDataTypeEnum.STRING); - tempCellData.setFormula(Boolean.TRUE); - tempCellData.setFormulaValue(formulaValue); - break; - case NUMERIC: - this.cellData = new CellData(BigDecimal.valueOf(frec.getValue())); - this.cellData.setFormula(Boolean.TRUE); - this.cellData.setFormulaValue(formulaValue); - break; - case ERROR: - this.cellData = new CellData(CellDataTypeEnum.ERROR); - this.cellData.setStringValue(ERROR); - this.cellData.setFormula(Boolean.TRUE); - this.cellData.setFormulaValue(formulaValue); - break; - case BOOLEAN: - this.cellData = new CellData(frec.getCachedBooleanValue()); - this.cellData.setFormula(Boolean.TRUE); - this.cellData.setFormulaValue(formulaValue); - break; - default: - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - this.cellData.setFormula(Boolean.TRUE); - this.cellData.setFormulaValue(formulaValue); - break; - } - } else if (record.getSid() == StringRecord.sid) { - if (outputNextStringRecord) { - // String for formula - StringRecord srec = (StringRecord)record; - this.cellData = tempCellData; - this.cellData.setStringValue(srec.getString()); - this.row = nextRow; - this.column = nextColumn; - outputNextStringRecord = false; - tempCellData = null; - } + public void processRecord(XlsReadContext xlsReadContext, Record record) { + FormulaRecord frec = (FormulaRecord)record; + Map cellMap = xlsReadContext.xlsReadSheetHolder().getCellMap(); + CellData tempCellData = new CellData(); + tempCellData.setRowIndex(frec.getRow()); + tempCellData.setColumnIndex((int)frec.getColumn()); + CellType cellType = CellType.forInt(frec.getCachedResultType()); + String formulaValue = null; + try { + formulaValue = HSSFFormulaParser.toFormulaString(xlsReadContext.xlsReadWorkbookHolder().getHssfWorkbook(), + frec.getParsedExpression()); + } catch (Exception e) { + LOGGER.debug("Get formula value error.", e); + } + tempCellData.setFormula(Boolean.TRUE); + tempCellData.setFormulaValue(formulaValue); + switch (cellType) { + case STRING: + // Formula result is a string + // This is stored in the next record + tempCellData.setType(CellDataTypeEnum.STRING); + xlsReadContext.xlsReadSheetHolder().setTempCellData(tempCellData); + break; + case NUMERIC: + tempCellData.setType(CellDataTypeEnum.NUMBER); + tempCellData.setNumberValue(BigDecimal.valueOf(frec.getValue())); + Integer dataFormat = + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec); + tempCellData.setDataFormat(dataFormat); + tempCellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat, + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatString(frec), + xlsReadContext.readSheetHolder().getGlobalConfiguration().getLocale())); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + case ERROR: + tempCellData.setType(CellDataTypeEnum.ERROR); + tempCellData.setStringValue(ERROR); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + case BOOLEAN: + tempCellData.setType(CellDataTypeEnum.BOOLEAN); + tempCellData.setBooleanValue(frec.getCachedBooleanValue()); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + default: + tempCellData.setType(CellDataTypeEnum.EMPTY); + cellMap.put((int)frec.getColumn(), tempCellData); + break; } - } - - @Override - public void init() { - this.nextRow = 0; - this.nextColumn = 0; - } - - @Override - public int getOrder() { - return 0; } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java new file mode 100644 index 0000000..99c3a80 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.HyperlinkRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class HyperlinkRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + HyperlinkRecord hr = (HyperlinkRecord)record; + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.HYPERLINK, hr.getAddress(), hr.getFirstRow(), + hr.getLastRow(), hr.getFirstColumn(), hr.getLastColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java index 6837ebd..b95812e 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java @@ -3,40 +3,20 @@ package com.alibaba.excel.analysis.v03.handlers; import org.apache.poi.hssf.record.IndexRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; /** * Record handler * * @author Jiaju Zhuang */ -public class IndexRecordHandler extends AbstractXlsRecordHandler { - - private AnalysisContext context; - - public IndexRecordHandler(AnalysisContext context) { - this.context = context; - } - - @Override - public boolean support(Record record) { - return record instanceof IndexRecord; - } - - @Override - public void init() {} - +public class IndexRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public void processRecord(Record record) { - if (context.readSheetHolder() == null) { + public void processRecord(XlsReadContext xlsReadContext, Record record) { + if (xlsReadContext.readSheetHolder() == null) { return; } - context.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1()); - } - - @Override - public int getOrder() { - return 1; + xlsReadContext.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1()); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java index 35732c1..4f63ab0 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java @@ -3,7 +3,9 @@ package com.alibaba.excel.analysis.v03.handlers; import org.apache.poi.hssf.record.LabelRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.metadata.CellData; /** @@ -11,27 +13,16 @@ import com.alibaba.excel.metadata.CellData; * * @author Dan Zheng */ -public class LabelRecordHandler extends AbstractXlsRecordHandler { +public class LabelRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public boolean support(Record record) { - return LabelRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { + public void processRecord(XlsReadContext xlsReadContext, Record record) { LabelRecord lrec = (LabelRecord)record; - this.row = lrec.getRow(); - this.column = lrec.getColumn(); - this.cellData = new CellData(lrec.getValue()); - } - - @Override - public void init() { - - } - - @Override - public int getOrder() { - return 0; + String data = lrec.getValue(); + if (data != null && xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + data = data.trim(); + } + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)lrec.getColumn(), + CellData.newInstance(data, lrec.getRow(), (int)lrec.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java new file mode 100644 index 0000000..7fb5cd0 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.Map; + +import org.apache.poi.hssf.record.LabelSSTRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.CellData; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class LabelSstRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + LabelSSTRecord lsrec = (LabelSSTRecord)record; + ReadCache readCache = xlsReadContext.readWorkbookHolder().getReadCache(); + Map cellMap = xlsReadContext.xlsReadSheetHolder().getCellMap(); + if (readCache == null) { + cellMap.put((int)lsrec.getColumn(), CellData.newEmptyInstance(lsrec.getRow(), (int)lsrec.getColumn())); + return; + } + String data = readCache.get(lsrec.getSSTIndex()); + if (data == null) { + cellMap.put((int)lsrec.getColumn(), CellData.newEmptyInstance(lsrec.getRow(), (int)lsrec.getColumn())); + return; + } + if (xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + data = data.trim(); + } + cellMap.put((int)lsrec.getColumn(), CellData.newInstance(data, lsrec.getRow(), (int)lsrec.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java new file mode 100644 index 0000000..f9bf472 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.ss.util.CellRangeAddress; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class MergeCellsRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.MERGE); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + MergeCellsRecord mcr = (MergeCellsRecord)record; + for (int i = 0; i < mcr.getNumAreas(); i++) { + CellRangeAddress cellRangeAddress = mcr.getAreaAt(i); + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.MERGE, null, cellRangeAddress.getFirstRow(), + cellRangeAddress.getLastRow(), cellRangeAddress.getFirstColumn(), cellRangeAddress.getLastColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); + } + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/MissingCellDummyRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/MissingCellDummyRecordHandler.java deleted file mode 100644 index c879504..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/MissingCellDummyRecordHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.alibaba.excel.analysis.v03.handlers; - -import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; -import org.apache.poi.hssf.record.Record; - -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; - -/** - * Record handler - * - * @author Dan Zheng - */ -public class MissingCellDummyRecordHandler extends AbstractXlsRecordHandler { - @Override - public boolean support(Record record) { - return record instanceof MissingCellDummyRecord; - } - - @Override - public void init() { - - } - - @Override - public void processRecord(Record record) { - MissingCellDummyRecord mcdr = (MissingCellDummyRecord)record; - this.row = mcdr.getRow(); - this.column = mcdr.getColumn(); - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - } - - @Override - public int getOrder() { - return 1; - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java index 9ab800c..6b6d803 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java @@ -3,36 +3,29 @@ package com.alibaba.excel.analysis.v03.handlers; import org.apache.poi.hssf.record.NoteRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; /** * Record handler * * @author Dan Zheng */ -public class NoteRecordHandler extends AbstractXlsRecordHandler { - @Override - public boolean support(Record record) { - return NoteRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - NoteRecord nrec = (NoteRecord)record; - this.row = nrec.getRow(); - this.column = nrec.getColumn(); - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - } +public class NoteRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public void init() { - + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT); } @Override - public int getOrder() { - return 0; + public void processRecord(XlsReadContext xlsReadContext, Record record) { + NoteRecord nr = (NoteRecord)record; + String text = xlsReadContext.xlsReadSheetHolder().getObjectCacheMap().get(nr.getShapeId()); + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.COMMENT, text, nr.getRow(), nr.getColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java index 75c3128..5060809 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java @@ -2,11 +2,13 @@ package com.alibaba.excel.analysis.v03.handlers; import java.math.BigDecimal; -import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.record.NumberRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.metadata.CellData; /** @@ -14,35 +16,18 @@ import com.alibaba.excel.metadata.CellData; * * @author Dan Zheng */ -public class NumberRecordHandler extends AbstractXlsRecordHandler { - private FormatTrackingHSSFListener formatListener; - - public NumberRecordHandler(FormatTrackingHSSFListener formatListener) { - this.formatListener = formatListener; - } - - @Override - public boolean support(Record record) { - return NumberRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - NumberRecord numrec = (NumberRecord)record; - this.row = numrec.getRow(); - this.column = numrec.getColumn(); - this.cellData = new CellData(BigDecimal.valueOf(numrec.getValue())); - this.cellData.setDataFormat(formatListener.getFormatIndex(numrec)); - this.cellData.setDataFormatString(formatListener.getFormatString(numrec)); - } - - @Override - public void init() { - - } +public class NumberRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public int getOrder() { - return 0; + public void processRecord(XlsReadContext xlsReadContext, Record record) { + NumberRecord nr = (NumberRecord)record; + CellData cellData = CellData.newInstance(BigDecimal.valueOf(nr.getValue()), nr.getRow(), (int)nr.getColumn()); + Integer dataFormat = xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(nr); + cellData.setDataFormat(dataFormat); + cellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat, + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatString(nr), + xlsReadContext.readSheetHolder().getGlobalConfiguration().getLocale())); + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)nr.getColumn(), cellData); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java new file mode 100644 index 0000000..97bcd20 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.CommonObjectDataSubRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SubRecord; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Jiaju Zhuang + */ +public class ObjRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + ObjRecord or = (ObjRecord)record; + for (SubRecord subRecord : or.getSubRecords()) { + if (subRecord instanceof CommonObjectDataSubRecord) { + CommonObjectDataSubRecord codsr = (CommonObjectDataSubRecord)subRecord; + if (CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT == codsr.getObjectType()) { + xlsReadContext.xlsReadSheetHolder().setTempObjectIndex(codsr.getObjectId()); + } + break; + } + } + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java index 140fb72..d8317b3 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java @@ -3,8 +3,8 @@ package com.alibaba.excel.analysis.v03.handlers; import org.apache.poi.hssf.record.RKRecord; import org.apache.poi.hssf.record.Record; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.metadata.CellData; /** @@ -12,28 +12,12 @@ import com.alibaba.excel.metadata.CellData; * * @author Dan Zheng */ -public class RkRecordHandler extends AbstractXlsRecordHandler { - @Override - public boolean support(Record record) { - return RKRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - RKRecord rkrec = (RKRecord)record; - - this.row = rkrec.getRow(); - this.row = rkrec.getColumn(); - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - } - - @Override - public void init() { - - } +public class RkRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public int getOrder() { - return 0; + public void processRecord(XlsReadContext xlsReadContext, Record record) { + RKRecord re = (RKRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)re.getColumn(), + CellData.newEmptyInstance(re.getRow(), (int)re.getColumn())); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java index 5c1c8ae..35727b6 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java @@ -1,49 +1,20 @@ package com.alibaba.excel.analysis.v03.handlers; -import org.apache.poi.hssf.record.LabelSSTRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.SSTRecord; -import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.cache.XlsCache; +import com.alibaba.excel.context.xls.XlsReadContext; /** * Record handler * * @author Dan Zheng */ -public class SstRecordHandler extends AbstractXlsRecordHandler { - private SSTRecord sstRecord; - - @Override - public boolean support(Record record) { - return SSTRecord.sid == record.getSid() || LabelSSTRecord.sid == record.getSid(); - } - - @Override - public void processRecord(Record record) { - if (record.getSid() == SSTRecord.sid) { - sstRecord = (SSTRecord)record; - } else if (record.getSid() == LabelSSTRecord.sid) { - LabelSSTRecord lsrec = (LabelSSTRecord)record; - this.row = lsrec.getRow(); - this.column = lsrec.getColumn(); - if (sstRecord == null) { - this.cellData = new CellData(CellDataTypeEnum.EMPTY); - } else { - this.cellData = new CellData(sstRecord.getString(lsrec.getSSTIndex()).toString()); - } - } - } - - @Override - public void init() { - - } - +public class SstRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { @Override - public int getOrder() { - return 0; + public void processRecord(XlsReadContext xlsReadContext, Record record) { + xlsReadContext.readWorkbookHolder().setReadCache(new XlsCache((SSTRecord)record)); } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java new file mode 100644 index 0000000..3b95cfd --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.StringRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class StringRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(StringRecordHandler.class); + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + // String for formula + StringRecord srec = (StringRecord)record; + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + CellData tempCellData = xlsReadSheetHolder.getTempCellData(); + if (tempCellData == null) { + LOGGER.warn("String type formula but no value found."); + return; + } + tempCellData.setStringValue(srec.getString()); + xlsReadSheetHolder.getCellMap().put(tempCellData.getColumnIndex(), tempCellData); + xlsReadSheetHolder.setTempCellData(null); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java new file mode 100644 index 0000000..949498f --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.TextObjectRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +/** + * Record handler + * + * @author Jiaju Zhuang + */ +public class TextObjectRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(TextObjectRecordHandler.class); + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + TextObjectRecord tor = (TextObjectRecord)record; + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + Integer tempObjectIndex = xlsReadSheetHolder.getTempObjectIndex(); + if (tempObjectIndex == null) { + LOGGER.debug("tempObjectIndex is null."); + return; + } + xlsReadSheetHolder.getObjectCacheMap().put(tempObjectIndex, tor.getStr().getString()); + xlsReadSheetHolder.setTempObjectIndex(null); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxCellHandler.java deleted file mode 100644 index a4db7f4..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxCellHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import org.xml.sax.Attributes; - -/** - * Cell handler - * - * @author Dan Zheng - */ -public interface XlsxCellHandler { - /** - * Which tags are supported - * - * @param name - * Tag name - * @return Support parsing or not - */ - boolean support(String name); - - /** - * Start handle - * - * @param name - * Tag name - * @param attributes - * Tag attributes - */ - void startHandle(String name, Attributes attributes); - - /** - * End handle - * - * @param name - * Tag name - */ - void endHandle(String name); -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxHandlerFactory.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxHandlerFactory.java deleted file mode 100644 index 82f7d2c..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxHandlerFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.poi.xssf.model.StylesTable; - -import com.alibaba.excel.analysis.v07.handlers.CountRowCellHandler; -import com.alibaba.excel.analysis.v07.handlers.DefaultCellHandler; -import com.alibaba.excel.analysis.v07.handlers.ProcessResultCellHandler; -import com.alibaba.excel.context.AnalysisContext; - -/** - * Build handler - * - * @author Dan Zheng - */ -public class XlsxHandlerFactory { - public static List buildCellHandlers(AnalysisContext analysisContext, StylesTable stylesTable) { - List result = new ArrayList(); - result.add(new CountRowCellHandler(analysisContext)); - DefaultCellHandler defaultCellHandler = new DefaultCellHandler(analysisContext, stylesTable); - result.add(defaultCellHandler); - result.add(new ProcessResultCellHandler(analysisContext, defaultCellHandler)); - return result; - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java deleted file mode 100644 index ececb47..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import java.util.List; - -import org.apache.poi.xssf.model.StylesTable; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import com.alibaba.excel.context.AnalysisContext; - -/** - * - * @author jipengfei - */ -public class XlsxRowHandler extends DefaultHandler { - - private List cellHandlers; - private XlsxRowResultHolder rowResultHolder; - - public XlsxRowHandler(AnalysisContext analysisContext, StylesTable stylesTable) { - this.cellHandlers = XlsxHandlerFactory.buildCellHandlers(analysisContext, stylesTable); - for (XlsxCellHandler cellHandler : cellHandlers) { - if (cellHandler instanceof XlsxRowResultHolder) { - this.rowResultHolder = (XlsxRowResultHolder)cellHandler; - break; - } - } - } - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { - for (XlsxCellHandler cellHandler : cellHandlers) { - if (cellHandler.support(name)) { - cellHandler.startHandle(name, attributes); - } - } - } - - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - for (XlsxCellHandler cellHandler : cellHandlers) { - if (cellHandler.support(name)) { - cellHandler.endHandle(name); - } - } - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (rowResultHolder != null) { - rowResultHolder.appendCurrentCellValue(ch, start, length); - } - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java deleted file mode 100644 index d13da73..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowResultHolder.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import java.util.Map; - -import com.alibaba.excel.metadata.CellData; - -/** - * Result holder - * - * @author jipengfei - */ -public interface XlsxRowResultHolder { - /** - * Clear Result - */ - void clearResult(); - - /** - * Append current 'cellValue' - * - * @param ch - * @param start - * @param length - */ - void appendCurrentCellValue(char[] ch, int start, int length); - - /** - * Get row content - * - * @return - */ - Map getCurRowContent(); -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index 2862be2..a0a9af0 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -15,8 +15,10 @@ import javax.xml.parsers.SAXParserFactory; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.ss.util.CellAddress; import org.apache.poi.xssf.eventusermodel.XSSFReader; -import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.model.CommentsTable; +import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.xssf.usermodel.XSSFRelation; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr; @@ -26,37 +28,40 @@ import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import com.alibaba.excel.analysis.ExcelReadExecutor; +import com.alibaba.excel.analysis.v07.handlers.sax.SharedStringsTableHandler; +import com.alibaba.excel.analysis.v07.handlers.sax.XlsxRowHandler; import com.alibaba.excel.cache.ReadCache; -import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.metadata.CellExtra; import com.alibaba.excel.read.metadata.ReadSheet; -import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.SheetUtils; import com.alibaba.excel.util.StringUtils; /** - * * @author jipengfei */ public class XlsxSaxAnalyser implements ExcelReadExecutor { - private AnalysisContext analysisContext; + private XlsxReadContext xlsxReadContext; private List sheetList; private Map sheetMap; /** - * Current style information + * excel comments key: sheetNo value: CommentsTable */ - private StylesTable stylesTable; + private Map commentsTableMap; - public XlsxSaxAnalyser(AnalysisContext analysisContext, InputStream decryptedStream) throws Exception { - this.analysisContext = analysisContext; + public XlsxSaxAnalyser(XlsxReadContext xlsxReadContext, InputStream decryptedStream) throws Exception { + this.xlsxReadContext = xlsxReadContext; // Initialize cache - ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + XlsxReadWorkbookHolder xlsxReadWorkbookHolder = xlsxReadContext.xlsxReadWorkbookHolder(); - OPCPackage pkg = readOpcPackage(readWorkbookHolder, decryptedStream); - readWorkbookHolder.setOpcPackage(pkg); + OPCPackage pkg = readOpcPackage(xlsxReadWorkbookHolder, decryptedStream); + xlsxReadWorkbookHolder.setOpcPackage(pkg); ArrayList packageParts = pkg.getPartsByContentType(XSSFRelation.SHARED_STRINGS.getContentType()); @@ -64,18 +69,19 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { PackagePart sharedStringsTablePackagePart = packageParts.get(0); // Specify default cache - defaultReadCache(readWorkbookHolder, sharedStringsTablePackagePart); + defaultReadCache(xlsxReadWorkbookHolder, sharedStringsTablePackagePart); // Analysis sharedStringsTable.xml - analysisSharedStringsTable(sharedStringsTablePackagePart.getInputStream(), readWorkbookHolder); + analysisSharedStringsTable(sharedStringsTablePackagePart.getInputStream(), xlsxReadWorkbookHolder); } XSSFReader xssfReader = new XSSFReader(pkg); - analysisUse1904WindowDate(xssfReader, readWorkbookHolder); + analysisUse1904WindowDate(xssfReader, xlsxReadWorkbookHolder); - stylesTable = xssfReader.getStylesTable(); + xlsxReadWorkbookHolder.setStylesTable(xssfReader.getStylesTable()); sheetList = new ArrayList(); sheetMap = new HashMap(); + commentsTableMap = new HashMap(); XSSFReader.SheetIterator ite = (XSSFReader.SheetIterator)xssfReader.getSheetsData(); int index = 0; if (!ite.hasNext()) { @@ -85,19 +91,26 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { InputStream inputStream = ite.next(); sheetList.add(new ReadSheet(index, ite.getSheetName())); sheetMap.put(index, inputStream); + if (xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) { + CommentsTable commentsTable = ite.getSheetComments(); + if (null != commentsTable) { + commentsTableMap.put(index, commentsTable); + } + } index++; } } - private void defaultReadCache(ReadWorkbookHolder readWorkbookHolder, PackagePart sharedStringsTablePackagePart) { - ReadCache readCache = readWorkbookHolder.getReadCacheSelector().readCache(sharedStringsTablePackagePart); - readWorkbookHolder.setReadCache(readCache); - readCache.init(analysisContext); + private void defaultReadCache(XlsxReadWorkbookHolder xlsxReadWorkbookHolder, + PackagePart sharedStringsTablePackagePart) { + ReadCache readCache = xlsxReadWorkbookHolder.getReadCacheSelector().readCache(sharedStringsTablePackagePart); + xlsxReadWorkbookHolder.setReadCache(readCache); + readCache.init(xlsxReadContext); } - private void analysisUse1904WindowDate(XSSFReader xssfReader, ReadWorkbookHolder readWorkbookHolder) + private void analysisUse1904WindowDate(XSSFReader xssfReader, XlsxReadWorkbookHolder xlsxReadWorkbookHolder) throws Exception { - if (readWorkbookHolder.globalConfiguration().getUse1904windowing() != null) { + if (xlsxReadWorkbookHolder.globalConfiguration().getUse1904windowing() != null) { return; } InputStream workbookXml = xssfReader.getWorkbookData(); @@ -105,38 +118,38 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { CTWorkbook wb = ctWorkbook.getWorkbook(); CTWorkbookPr prefix = wb.getWorkbookPr(); if (prefix != null && prefix.getDate1904()) { - readWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.TRUE); + xlsxReadWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.TRUE); } else { - readWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); + xlsxReadWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); } } private void analysisSharedStringsTable(InputStream sharedStringsTableInputStream, - ReadWorkbookHolder readWorkbookHolder) throws Exception { - ContentHandler handler = new SharedStringsTableHandler(readWorkbookHolder.getReadCache()); + XlsxReadWorkbookHolder xlsxReadWorkbookHolder) throws Exception { + ContentHandler handler = new SharedStringsTableHandler(xlsxReadWorkbookHolder.getReadCache()); parseXmlSource(sharedStringsTableInputStream, handler); - readWorkbookHolder.getReadCache().putFinished(); + xlsxReadWorkbookHolder.getReadCache().putFinished(); } - private OPCPackage readOpcPackage(ReadWorkbookHolder readWorkbookHolder, InputStream decryptedStream) + private OPCPackage readOpcPackage(XlsxReadWorkbookHolder xlsxReadWorkbookHolder, InputStream decryptedStream) throws Exception { - if (decryptedStream == null && readWorkbookHolder.getFile() != null) { - return OPCPackage.open(readWorkbookHolder.getFile()); + if (decryptedStream == null && xlsxReadWorkbookHolder.getFile() != null) { + return OPCPackage.open(xlsxReadWorkbookHolder.getFile()); } - if (readWorkbookHolder.getMandatoryUseInputStream()) { + if (xlsxReadWorkbookHolder.getMandatoryUseInputStream()) { if (decryptedStream != null) { return OPCPackage.open(decryptedStream); } else { - return OPCPackage.open(readWorkbookHolder.getInputStream()); + return OPCPackage.open(xlsxReadWorkbookHolder.getInputStream()); } } File readTempFile = FileUtils.createCacheTmpFile(); - readWorkbookHolder.setTempFile(readTempFile); + xlsxReadWorkbookHolder.setTempFile(readTempFile); File tempFile = new File(readTempFile.getPath(), UUID.randomUUID().toString() + ".xlsx"); if (decryptedStream != null) { FileUtils.writeToFile(tempFile, decryptedStream); } else { - FileUtils.writeToFile(tempFile, readWorkbookHolder.getInputStream()); + FileUtils.writeToFile(tempFile, xlsxReadWorkbookHolder.getInputStream()); } return OPCPackage.open(tempFile, PackageAccess.READ); } @@ -150,7 +163,7 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { InputSource inputSource = new InputSource(inputStream); try { SAXParserFactory saxFactory; - String xlsxSAXParserFactoryName = analysisContext.readWorkbookHolder().getXlsxSAXParserFactoryName(); + String xlsxSAXParserFactoryName = xlsxReadContext.xlsxReadWorkbookHolder().getSaxParserFactoryName(); if (StringUtils.isEmpty(xlsxSAXParserFactoryName)) { saxFactory = SAXParserFactory.newInstance(); } else { @@ -180,16 +193,34 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { } @Override - public void execute(List readSheetList, Boolean readAll) { + public void execute() { for (ReadSheet readSheet : sheetList) { - readSheet = SheetUtils.match(readSheet, readSheetList, readAll, - analysisContext.readWorkbookHolder().getGlobalConfiguration()); + readSheet = SheetUtils.match(readSheet, xlsxReadContext); if (readSheet != null) { - analysisContext.currentSheet(readSheet); - parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(analysisContext, stylesTable)); + xlsxReadContext.currentSheet(readSheet); + parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(xlsxReadContext)); + // Read comments + readComments(readSheet); // The last sheet is read - analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + xlsxReadContext.analysisEventProcessor().endSheet(xlsxReadContext); } } } + + private void readComments(ReadSheet readSheet) { + if (!xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) { + return; + } + CommentsTable commentsTable = commentsTableMap.get(readSheet.getSheetNo()); + if (commentsTable == null) { + return; + } + Map cellComments = commentsTable.getCellComments(); + for (XSSFComment xssfComment : cellComments.values()) { + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.COMMENT, xssfComment.getString().toString(), + xssfComment.getRow(), xssfComment.getColumn()); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + } + } } diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java new file mode 100644 index 0000000..3521e46 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import java.math.BigDecimal; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.util.BooleanUtils; + +/** + * Cell Value Handler + * + * @author jipengfei + */ +public abstract class AbstractCellValueTagHandler extends AbstractXlsxTagHandler { + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + CellData tempCellData = xlsxReadSheetHolder.getTempCellData(); + StringBuilder tempData = xlsxReadSheetHolder.getTempData(); + CellDataTypeEnum oldType = tempCellData.getType(); + switch (oldType) { + case DIRECT_STRING: + case STRING: + case ERROR: + tempCellData.setStringValue(tempData.toString()); + break; + case BOOLEAN: + tempCellData.setBooleanValue(BooleanUtils.valueOf(tempData.toString())); + break; + case NUMBER: + case EMPTY: + tempCellData.setType(CellDataTypeEnum.NUMBER); + tempCellData.setNumberValue(new BigDecimal(tempData.toString())); + break; + default: + throw new IllegalStateException("Cannot set values now"); + } + + // set string value + setStringValue(xlsxReadContext); + + if (tempCellData.getStringValue() != null + && xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + tempCellData.setStringValue(tempCellData.getStringValue()); + } + + tempCellData.checkEmpty(); + xlsxReadSheetHolder.getCellMap().put(xlsxReadSheetHolder.getColumnIndex(), tempCellData); + } + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + xlsxReadContext.xlsxReadSheetHolder().getTempData().append(ch, start, length); + } + + /** + * Set string value. + * + * @param xlsxReadContext + */ + protected abstract void setStringValue(XlsxReadContext xlsxReadContext); + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java new file mode 100644 index 0000000..169fe00 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java @@ -0,0 +1,32 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Abstract tag handler + * + * @author Jiaju Zhuang + */ +public abstract class AbstractXlsxTagHandler implements XlsxTagHandler { + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return true; + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + + } + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java new file mode 100644 index 0000000..e153a3b --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java @@ -0,0 +1,32 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CellFormulaTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadSheetHolder.getTempCellData().setFormula(Boolean.TRUE); + xlsxReadSheetHolder.setTempFormula(new StringBuilder()); + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadSheetHolder.getTempCellData().setFormulaValue(xlsxReadSheetHolder.getTempFormula().toString()); + } + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + xlsxReadContext.xlsxReadSheetHolder().getTempFormula().append(ch, start, length); + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java new file mode 100644 index 0000000..277348a --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.apache.poi.xssf.usermodel.XSSFRichTextString; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.metadata.CellData; + +/** + * Cell inline string value handler + * + * @author jipengfei + */ +public class CellInlineStringValueTagHandler extends AbstractCellValueTagHandler { + + @Override + protected void setStringValue(XlsxReadContext xlsxReadContext) { + // This is a special form of string + CellData tempCellData = xlsxReadContext.xlsxReadSheetHolder().getTempCellData(); + XSSFRichTextString richTextString = new XSSFRichTextString(tempCellData.getStringValue()); + tempCellData.setStringValue(richTextString.toString()); + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java new file mode 100644 index 0000000..fe5a642 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java @@ -0,0 +1,57 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.util.PositionUtils; +import com.alibaba.excel.util.StringUtils; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CellTagHandler extends AbstractXlsxTagHandler { + + private static final int DEFAULT_FORMAT_INDEX = 0; + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadSheetHolder.setColumnIndex(PositionUtils.getCol(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R), + xlsxReadSheetHolder.getColumnIndex())); + + // 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 + // t="n" ,it's means Number + // t is null ,it's means Empty or Number + CellDataTypeEnum type = CellDataTypeEnum.buildFromCellType(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_T)); + xlsxReadSheetHolder.setTempCellData(new CellData(type)); + xlsxReadSheetHolder.setTempData(new StringBuilder()); + + // Put in data transformation information + String dateFormatIndex = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_S); + Integer dateFormatIndexInteger; + if (StringUtils.isEmpty(dateFormatIndex)) { + dateFormatIndexInteger = DEFAULT_FORMAT_INDEX; + } else { + dateFormatIndexInteger = Integer.parseInt(dateFormatIndex); + } + XSSFCellStyle xssfCellStyle = + xlsxReadContext.xlsxReadWorkbookHolder().getStylesTable().getStyleAt(dateFormatIndexInteger); + int dataFormat = xssfCellStyle.getDataFormat(); + xlsxReadSheetHolder.getTempCellData().setDataFormat(dataFormat); + xlsxReadSheetHolder.getTempCellData().setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat, + xssfCellStyle.getDataFormatString(), xlsxReadSheetHolder.getGlobalConfiguration().getLocale())); + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java new file mode 100644 index 0000000..09c9264 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; + +/** + * Cell Value Handler + * + * @author jipengfei + */ +public class CellValueTagHandler extends AbstractCellValueTagHandler { + + @Override + protected void setStringValue(XlsxReadContext xlsxReadContext) { + // Have to go "sharedStrings.xml" and get it + CellData tempCellData = xlsxReadContext.xlsxReadSheetHolder().getTempCellData(); + switch (tempCellData.getType()) { + case STRING: + String stringValue = xlsxReadContext.readWorkbookHolder().getReadCache() + .get(Integer.valueOf(tempCellData.getStringValue())); + if (stringValue != null && xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + stringValue = stringValue.trim(); + } + tempCellData.setStringValue(stringValue); + break; + case DIRECT_STRING: + tempCellData.setType(CellDataTypeEnum.STRING); + break; + default: + } + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java deleted file mode 100644 index b94483f..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.alibaba.excel.analysis.v07.handlers; - -import static com.alibaba.excel.constant.ExcelXmlConstants.DIMENSION; -import static com.alibaba.excel.constant.ExcelXmlConstants.DIMENSION_REF; - -import org.xml.sax.Attributes; - -import com.alibaba.excel.analysis.v07.XlsxCellHandler; -import com.alibaba.excel.context.AnalysisContext; - -/** - * Cell Handler - * - * @author jipengfei - */ -public class CountRowCellHandler implements XlsxCellHandler { - - private final AnalysisContext analysisContext; - - public CountRowCellHandler(AnalysisContext analysisContext) { - this.analysisContext = analysisContext; - } - - @Override - public boolean support(String name) { - return DIMENSION.equals(name); - } - - @Override - public void startHandle(String name, Attributes attributes) { - String d = attributes.getValue(DIMENSION_REF); - String totalStr = d.substring(d.indexOf(":") + 1, d.length()); - String c = totalStr.toUpperCase().replaceAll("[A-Z]", ""); - analysisContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c)); - } - - @Override - public void endHandle(String name) { - - } - -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java new file mode 100644 index 0000000..823bafb --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CountTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String d = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + String totalStr = d.substring(d.indexOf(":") + 1, d.length()); + String c = totalStr.toUpperCase().replaceAll("[A-Z]", ""); + xlsxReadContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c)); + } + +} 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 deleted file mode 100644 index e53c3cc..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java +++ /dev/null @@ -1,186 +0,0 @@ -package com.alibaba.excel.analysis.v07.handlers; - -import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_DATA_FORMAT_TAG; -import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_FORMULA_TAG; -import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_INLINE_STRING_VALUE_TAG; -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.math.BigDecimal; -import java.util.Deque; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map; - -import org.apache.poi.ss.usermodel.BuiltinFormats; -import org.apache.poi.xssf.model.StylesTable; -import org.apache.poi.xssf.usermodel.XSSFCellStyle; -import org.apache.poi.xssf.usermodel.XSSFRichTextString; -import org.xml.sax.Attributes; - -import com.alibaba.excel.analysis.v07.XlsxCellHandler; -import com.alibaba.excel.analysis.v07.XlsxRowResultHolder; -import com.alibaba.excel.constant.ExcelXmlConstants; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.util.BooleanUtils; -import com.alibaba.excel.util.PositionUtils; - -/** - * Cell Handler - * - * @author jipengfei - */ -public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder { - private final AnalysisContext analysisContext; - private Deque currentTagDeque = new LinkedList(); - private int curCol = -1; - private Map curRowContent = new LinkedHashMap(); - private CellData currentCellData; - private StringBuilder dataStringBuilder; - private StringBuilder formulaStringBuilder; - - /** - * Current style information - */ - private StylesTable stylesTable; - - public DefaultCellHandler(AnalysisContext analysisContext, StylesTable stylesTable) { - this.analysisContext = analysisContext; - this.stylesTable = stylesTable; - } - - @Override - public void clearResult() { - curRowContent = new LinkedHashMap(); - curCol=-1; - } - - @Override - public boolean support(String name) { - return CELL_VALUE_TAG.equals(name) || CELL_FORMULA_TAG.equals(name) || CELL_INLINE_STRING_VALUE_TAG.equals(name) - || CELL_TAG.equals(name); - } - - @Override - public void startHandle(String name, Attributes attributes) { - currentTagDeque.push(name); - // start a cell - if (CELL_TAG.equals(name)) { - curCol = PositionUtils.getCol(attributes.getValue(ExcelXmlConstants.POSITION),curCol); - - - // 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 - // t="n" ,it's means Number - // t is null ,it's means Empty or Number - CellDataTypeEnum type = CellDataTypeEnum.buildFromCellType(attributes.getValue(CELL_VALUE_TYPE_TAG)); - currentCellData = new CellData(type); - dataStringBuilder = new StringBuilder(); - - // Put in data transformation information - String dateFormatIndex = attributes.getValue(CELL_DATA_FORMAT_TAG); - if (dateFormatIndex != null) { - int dateFormatIndexInteger = Integer.parseInt(dateFormatIndex); - XSSFCellStyle xssfCellStyle = stylesTable.getStyleAt(dateFormatIndexInteger); - int dataFormat = xssfCellStyle.getDataFormat(); - String dataFormatString = xssfCellStyle.getDataFormatString(); - currentCellData.setDataFormat(dataFormat); - if (dataFormatString == null) { - currentCellData.setDataFormatString(BuiltinFormats.getBuiltinFormat(dataFormat)); - } else { - currentCellData.setDataFormatString(dataFormatString); - } - } - } - // cell is formula - if (CELL_FORMULA_TAG.equals(name)) { - currentCellData.setFormula(Boolean.TRUE); - formulaStringBuilder = new StringBuilder(); - } - } - - @Override - public void endHandle(String name) { - currentTagDeque.pop(); - // cell is formula - if (CELL_FORMULA_TAG.equals(name)) { - currentCellData.setFormulaValue(formulaStringBuilder.toString()); - return; - } - if (CELL_VALUE_TAG.equals(name) || CELL_INLINE_STRING_VALUE_TAG.equals(name)) { - CellDataTypeEnum oldType = currentCellData.getType(); - switch (oldType) { - case DIRECT_STRING: - case STRING: - case ERROR: - currentCellData.setStringValue(dataStringBuilder.toString()); - break; - case BOOLEAN: - currentCellData.setBooleanValue(BooleanUtils.valueOf(dataStringBuilder.toString())); - break; - case NUMBER: - case EMPTY: - currentCellData.setType(CellDataTypeEnum.NUMBER); - currentCellData.setNumberValue(new BigDecimal(dataStringBuilder.toString())); - break; - default: - throw new IllegalStateException("Cannot set values now"); - } - - if (CELL_VALUE_TAG.equals(name)) { - // Have to go "sharedStrings.xml" and get it - if (currentCellData.getType() == CellDataTypeEnum.STRING) { - String stringValue = analysisContext.readWorkbookHolder().getReadCache() - .get(Integer.valueOf(currentCellData.getStringValue())); - if (stringValue != null - && analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) { - stringValue = stringValue.trim(); - } - currentCellData.setStringValue(stringValue); - } else if (currentCellData.getType() == CellDataTypeEnum.DIRECT_STRING) { - currentCellData.setType(CellDataTypeEnum.STRING); - } - } - // This is a special form of string - if (CELL_INLINE_STRING_VALUE_TAG.equals(name)) { - XSSFRichTextString richTextString = new XSSFRichTextString(currentCellData.getStringValue()); - String stringValue = richTextString.toString(); - if (stringValue != null && analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) { - stringValue = stringValue.trim(); - } - currentCellData.setStringValue(stringValue); - } - - currentCellData.checkEmpty(); - curRowContent.put(curCol, currentCellData); - } - } - - @Override - public void appendCurrentCellValue(char[] ch, int start, int length) { - String currentTag = currentTagDeque.peek(); - if (currentTag == null) { - return; - } - if (CELL_FORMULA_TAG.equals(currentTag)) { - formulaStringBuilder.append(ch, start, length); - return; - } - if (!CELL_VALUE_TAG.equals(currentTag) && !CELL_INLINE_STRING_VALUE_TAG.equals(currentTag)) { - return; - } - dataStringBuilder.append(ch, start, length); - } - - @Override - public Map getCurRowContent() { - return curRowContent; - } - -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java new file mode 100644 index 0000000..ee0154e --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.util.StringUtils; + +/** + * Cell Handler + * + * @author Jiaju Zhuang + */ +public class HyperlinkTagHandler extends AbstractXlsxTagHandler { + + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK); + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String ref = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + String location = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_LOCATION); + if (StringUtils.isEmpty(ref)) { + return; + } + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.HYPERLINK, location, ref); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java new file mode 100644 index 0000000..8b0fa04 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.util.StringUtils; + +/** + * Cell Handler + * + * @author Jiaju Zhuang + */ +public class MergeCellTagHandler extends AbstractXlsxTagHandler { + + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.MERGE); + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String ref = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + if (StringUtils.isEmpty(ref)) { + return; + } + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.MERGE, null, ref); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/ProcessResultCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/ProcessResultCellHandler.java deleted file mode 100644 index c854242..0000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/ProcessResultCellHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.alibaba.excel.analysis.v07.handlers; - -import static com.alibaba.excel.constant.ExcelXmlConstants.ROW_TAG; - -import org.xml.sax.Attributes; - -import com.alibaba.excel.analysis.v07.XlsxCellHandler; -import com.alibaba.excel.analysis.v07.XlsxRowResultHolder; -import com.alibaba.excel.constant.ExcelXmlConstants; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.read.listener.event.EachRowAnalysisFinishEvent; -import com.alibaba.excel.read.metadata.holder.ReadRowHolder; -import com.alibaba.excel.util.PositionUtils; - -/** - * Cell Handler - * - * @author jipengfei - */ -public class ProcessResultCellHandler implements XlsxCellHandler { - private AnalysisContext analysisContext; - private XlsxRowResultHolder rowResultHandler; - private int currentRow = -1; - - public ProcessResultCellHandler(AnalysisContext analysisContext, XlsxRowResultHolder rowResultHandler) { - this.analysisContext = analysisContext; - this.rowResultHandler = rowResultHandler; - } - - @Override - public boolean support(String name) { - return ROW_TAG.equals(name); - } - - @Override - public void startHandle(String name, Attributes attributes) { - currentRow = PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.POSITION),currentRow); - analysisContext.readRowHolder( - new ReadRowHolder(currentRow, analysisContext.readSheetHolder().getGlobalConfiguration())); - } - - @Override - public void endHandle(String name) { - analysisContext.readSheetHolder() - .notifyEndOneRow(new EachRowAnalysisFinishEvent(rowResultHandler.getCurRowContent()), analysisContext); - rowResultHandler.clearResult(); - } - -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java new file mode 100644 index 0000000..61f3873 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java @@ -0,0 +1,51 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import java.util.LinkedHashMap; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.util.PositionUtils; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class RowTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + int rowIndex = PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R), + xlsxReadSheetHolder.getRowIndex()); + Integer lastRowIndex = xlsxReadContext.readSheetHolder().getRowIndex(); + if (lastRowIndex != null) { + while (lastRowIndex + 1 < rowIndex) { + xlsxReadContext.readRowHolder(new ReadRowHolder(lastRowIndex + 1, RowTypeEnum.EMPTY, + xlsxReadSheetHolder.getGlobalConfiguration(), new LinkedHashMap())); + xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); + xlsxReadSheetHolder.setColumnIndex(null); + xlsxReadSheetHolder.setCellMap(new LinkedHashMap()); + lastRowIndex++; + } + } + xlsxReadSheetHolder.setRowIndex(rowIndex); + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadContext.readRowHolder(new ReadRowHolder(xlsxReadSheetHolder.getRowIndex(), RowTypeEnum.DATA, + xlsxReadSheetHolder.getGlobalConfiguration(), xlsxReadSheetHolder.getCellMap())); + xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); + xlsxReadSheetHolder.setColumnIndex(null); + xlsxReadSheetHolder.setCellMap(new LinkedHashMap()); + } + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java new file mode 100644 index 0000000..fe392f9 --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java @@ -0,0 +1,54 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Tag handler + * + * @author Dan Zheng + */ +public interface XlsxTagHandler { + + /** + * Whether to support + * + * @param xlsxReadContext + * @return + */ + boolean support(XlsxReadContext xlsxReadContext); + + /** + * Start handle + * + * @param xlsxReadContext + * xlsxReadContext + * @param name + * Tag name + * @param attributes + * Tag attributes + */ + void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes); + + /** + * End handle + * + * @param xlsxReadContext + * xlsxReadContext + * @param name + * Tag name + */ + void endElement(XlsxReadContext xlsxReadContext, String name); + + /** + * Read data + * + * @param xlsxReadContext + * @param ch + * @param start + * @param length + */ + void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length); + +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/SharedStringsTableHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java similarity index 97% rename from src/main/java/com/alibaba/excel/analysis/v07/SharedStringsTableHandler.java rename to src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java index 5e001ed..ea6ffc5 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/SharedStringsTableHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java @@ -1,4 +1,4 @@ -package com.alibaba.excel.analysis.v07; +package com.alibaba.excel.analysis.v07.handlers.sax; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java new file mode 100644 index 0000000..65eaa6a --- /dev/null +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java @@ -0,0 +1,93 @@ +package com.alibaba.excel.analysis.v07.handlers.sax; + +import java.util.HashMap; +import java.util.Map; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import com.alibaba.excel.analysis.v07.handlers.CellFormulaTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellInlineStringValueTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellValueTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CountTagHandler; +import com.alibaba.excel.analysis.v07.handlers.HyperlinkTagHandler; +import com.alibaba.excel.analysis.v07.handlers.MergeCellTagHandler; +import com.alibaba.excel.analysis.v07.handlers.RowTagHandler; +import com.alibaba.excel.analysis.v07.handlers.XlsxTagHandler; +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * @author jipengfei + */ +public class XlsxRowHandler extends DefaultHandler { + private XlsxReadContext xlsxReadContext; + private static final Map XLSX_CELL_HANDLER_MAP = new HashMap(32); + + static { + CellFormulaTagHandler cellFormulaTagHandler = new CellFormulaTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_FORMULA_TAG, cellFormulaTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_FORMULA_TAG, cellFormulaTagHandler); + CellInlineStringValueTagHandler cellInlineStringValueTagHandler = new CellInlineStringValueTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler); + CellTagHandler cellTagHandler = new CellTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_TAG, cellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_TAG, cellTagHandler); + CellValueTagHandler cellValueTagHandler = new CellValueTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_VALUE_TAG, cellValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_VALUE_TAG, cellValueTagHandler); + CountTagHandler countTagHandler = new CountTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.DIMENSION_TAG, countTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_DIMENSION_TAG, countTagHandler); + HyperlinkTagHandler hyperlinkTagHandler = new HyperlinkTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.HYPERLINK_TAG, hyperlinkTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_HYPERLINK_TAG, hyperlinkTagHandler); + MergeCellTagHandler mergeCellTagHandler = new MergeCellTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.MERGE_CELL_TAG, mergeCellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_MERGE_CELL_TAG, mergeCellTagHandler); + RowTagHandler rowTagHandler = new RowTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.ROW_TAG, rowTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_ROW_TAG, rowTagHandler); + } + + public XlsxRowHandler(XlsxReadContext xlsxReadContext) { + this.xlsxReadContext = xlsxReadContext; + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + xlsxReadContext.xlsxReadSheetHolder().getTagDeque().push(name); + handler.startElement(xlsxReadContext, name, attributes); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + String currentTag = xlsxReadContext.xlsxReadSheetHolder().getTagDeque().peek(); + if (currentTag == null) { + return; + } + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(currentTag); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + handler.characters(xlsxReadContext, ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String name) throws SAXException { + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + handler.endElement(xlsxReadContext, name); + xlsxReadContext.xlsxReadSheetHolder().getTagDeque().pop(); + } + +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java b/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java new file mode 100644 index 0000000..d593aef --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom content styles. + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentFontStyle { + + /** + * The name for the font (i.e. Arial) + */ + String fontName() default ""; + + /** + * Height in the familiar unit of measure - points + */ + short fontHeightInPoints() default -1; + + /** + * Whether to use italics or not + */ + boolean italic() default false; + + /** + * Whether to use a strikeout horizontal line through the text or not + */ + boolean strikeout() default false; + + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + short color() default -1; + + /** + * Set normal,super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + short typeOffset() default -1; + + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + byte underline() default -1; + + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + int charset() default -1; + + /** + * Bold + */ + boolean bold() default false; +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java b/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java new file mode 100644 index 0000000..af7bcec --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The regions of the loop merge + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentLoopMerge { + /** + * Each row + * + * @return + */ + int eachRow() default -1; + + /** + * Extend column + * + * @return + */ + int columnExtend() default 1; +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java b/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java new file mode 100644 index 0000000..9ef3f47 --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java @@ -0,0 +1,159 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * Custom content styles + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentStyle { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + short dataFormat() default -1; + + /** + * Set the cell's using this style to be hidden + */ + boolean hidden() default false; + + /** + * Set the cell's using this style to be locked + */ + boolean locked() default false; + + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + boolean quotePrefix() default false; + + /** + * Set the type of horizontal alignment for the cell + */ + HorizontalAlignment horizontalAlignment() default HorizontalAlignment.GENERAL; + + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + * + */ + boolean wrapped() default false; + + /** + * Set the type of vertical alignment for the cell + */ + VerticalAlignment verticalAlignment() default VerticalAlignment.CENTER; + + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + short rotation() default -1; + + /** + * Set the number of spaces to indent the text in the cell + */ + short indent() default -1; + + /** + * Set the type of border to use for the left border of the cell + */ + BorderStyle borderLeft() default BorderStyle.NONE; + + /** + * Set the type of border to use for the right border of the cell + */ + BorderStyle borderRight() default BorderStyle.NONE; + + /** + * Set the type of border to use for the top border of the cell + */ + BorderStyle borderTop() default BorderStyle.NONE; + + /** + * Set the type of border to use for the bottom border of the cell + */ + BorderStyle borderBottom() default BorderStyle.NONE; + + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + short leftBorderColor() default -1; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + * + */ + short rightBorderColor() default -1; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + * + */ + short topBorderColor() default -1; + + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + * + */ + short bottomBorderColor() default -1; + + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + FillPatternType fillPatternType() default FillPatternType.NO_FILL; + + /** + * Set the background fill color. + * + * @see IndexedColors + * + */ + short fillBackgroundColor() default -1; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + * + */ + short fillForegroundColor() default -1; + + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + boolean shrinkToFit() default false; + +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java b/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java new file mode 100644 index 0000000..957e50f --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom header styles. + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface HeadFontStyle { + + /** + * The name for the font (i.e. Arial) + */ + String fontName() default "宋体"; + + /** + * Height in the familiar unit of measure - points + */ + short fontHeightInPoints() default 14; + + /** + * Whether to use italics or not + */ + boolean italic() default false; + + /** + * Whether to use a strikeout horizontal line through the text or not + */ + boolean strikeout() default false; + + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + short color() default -1; + + /** + * Set normal,super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + short typeOffset() default -1; + + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + byte underline() default -1; + + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + int charset() default -1; + + /** + * Bold + */ + boolean bold() default true; +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java b/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java new file mode 100644 index 0000000..d882da8 --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java @@ -0,0 +1,159 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * Custom header styles + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface HeadStyle { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + short dataFormat() default -1; + + /** + * Set the cell's using this style to be hidden + */ + boolean hidden() default false; + + /** + * Set the cell's using this style to be locked + */ + boolean locked() default true; + + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + boolean quotePrefix() default false; + + /** + * Set the type of horizontal alignment for the cell + */ + HorizontalAlignment horizontalAlignment() default HorizontalAlignment.CENTER; + + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + * + */ + boolean wrapped() default true; + + /** + * Set the type of vertical alignment for the cell + */ + VerticalAlignment verticalAlignment() default VerticalAlignment.CENTER; + + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + short rotation() default -1; + + /** + * Set the number of spaces to indent the text in the cell + */ + short indent() default -1; + + /** + * Set the type of border to use for the left border of the cell + */ + BorderStyle borderLeft() default BorderStyle.THIN; + + /** + * Set the type of border to use for the right border of the cell + */ + BorderStyle borderRight() default BorderStyle.THIN; + + /** + * Set the type of border to use for the top border of the cell + */ + BorderStyle borderTop() default BorderStyle.THIN; + + /** + * Set the type of border to use for the bottom border of the cell + */ + BorderStyle borderBottom() default BorderStyle.THIN; + + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + short leftBorderColor() default -1; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + * + */ + short rightBorderColor() default -1; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + * + */ + short topBorderColor() default -1; + + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + * + */ + short bottomBorderColor() default -1; + + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + FillPatternType fillPatternType() default FillPatternType.SOLID_FOREGROUND; + + /** + * Set the background fill color. + * + * @see IndexedColors + * + */ + short fillBackgroundColor() default -1; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + * + */ + short fillForegroundColor() default -1; + + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + boolean shrinkToFit() default false; + +} diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java b/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java new file mode 100644 index 0000000..6c3a148 --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java @@ -0,0 +1,45 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Merge the cells once + * + * @author Jiaju Zhuang + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface OnceAbsoluteMerge { + /** + * First row + * + * @return + */ + int firstRowIndex() default -1; + + /** + * Last row + * + * @return + */ + int lastRowIndex() default -1; + + /** + * First column + * + * @return + */ + int firstColumnIndex() default -1; + + /** + * Last row + * + * @return + */ + int lastColumnIndex() default -1; +} diff --git a/src/main/java/com/alibaba/excel/cache/XlsCache.java b/src/main/java/com/alibaba/excel/cache/XlsCache.java new file mode 100644 index 0000000..9e814e0 --- /dev/null +++ b/src/main/java/com/alibaba/excel/cache/XlsCache.java @@ -0,0 +1,37 @@ +package com.alibaba.excel.cache; + +import org.apache.poi.hssf.record.SSTRecord; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * + * Use SSTRecord. + * + * @author Jiaju Zhuang + */ +public class XlsCache implements ReadCache { + private SSTRecord sstRecord; + + public XlsCache(SSTRecord sstRecord) { + this.sstRecord = sstRecord; + } + + @Override + public void init(AnalysisContext analysisContext) {} + + @Override + public void put(String value) {} + + @Override + public String get(Integer key) { + return sstRecord.getString(key).toString(); + } + + @Override + public void putFinished() {} + + @Override + public void destroy() {} + +} diff --git a/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java b/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java new file mode 100644 index 0000000..95eb0b8 --- /dev/null +++ b/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java @@ -0,0 +1,379 @@ +package com.alibaba.excel.constant; + +import java.util.Locale; + +/** + * Excel's built-in format conversion.Currently only supports Chinese. + * + *

+ * If it is not Chinese, it is recommended to directly modify the builtinFormats, which will better support + * internationalization in the future. + * + *

+ * Specific correspondence please see: + * https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.numberingformat?view=openxml-2.8.1 + * + * @author Jiaju Zhuang + **/ +public class BuiltinFormats { + + private static final String[] BUILTIN_FORMATS_CN = { + // 0 + "General", + // 1 + "0", + // 2 + "0.00", + // 3 + "#,##0", + // 4 + "#,##0.00", + // 5 + "\"¥\"#,##0_);(\"¥\"#,##0)", + // 6 + "\"¥\"#,##0_);[Red](\"¥\"#,##0)", + // 7 + "\"¥\"#,##0.00_);(\"¥\"#,##0.00)", + // 8 + "\"¥\"#,##0.00_);[Red](\"¥\"#,##0.00)", + // 9 + "0%", + // 10 + "0.00%", + // 11 + "0.00E+00", + // 12 + "# ?/?", + // 13 + "# ??/??", + // 14 + // The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d". + "yyyy/m/d", + // 15 + "d-mmm-yy", + // 16 + "d-mmm", + // 17 + "mmm-yy", + // 18 + "h:mm AM/PM", + // 19 + "h:mm:ss AM/PM", + // 20 + "h:mm", + // 21 + "h:mm:ss", + // 22 + // The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm". + "yyyy-m-d h:mm", + // 23-26 No specific correspondence found in the official documentation. + // 23 + null, + // 24 + null, + // 25 + null, + // 26 + null, + // 27 + "yyyy\"年\"m\"月\"", + // 28 + "m\"月\"d\"日\"", + // 29 + "m\"月\"d\"日\"", + // 30 + "m-d-yy", + // 31 + "yyyy\"年\"m\"月\"d\"日\"", + // 32 + "h\"时\"mm\"分\"", + // 33 + "h\"时\"mm\"分\"ss\"秒\"", + // 34 + "上午/下午h\"时\"mm\"分\"", + // 35 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 36 + "yyyy\"年\"m\"月\"", + // 37 + "#,##0_);(#,##0)", + // 38 + "#,##0_);[Red](#,##0)", + // 39 + "#,##0.00_);(#,##0.00)", + // 40 + "#,##0.00_);[Red](#,##0.00)", + // 41 + "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + // 42 + "_(\"¥\"* #,##0_);_(\"¥\"* (#,##0);_(\"¥\"* \"-\"_);_(@_)", + // 43 + "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + // 44 + "_(\"¥\"* #,##0.00_);_(\"¥\"* (#,##0.00);_(\"¥\"* \"-\"??_);_(@_)", + // 45 + "mm:ss", + // 46 + "[h]:mm:ss", + // 47 + "mm:ss.0", + // 48 + "##0.0E+0", + // 49 + "@", + // 50 + "yyyy\"年\"m\"月\"", + // 51 + "m\"月\"d\"日\"", + // 52 + "yyyy\"年\"m\"月\"", + // 53 + "m\"月\"d\"日\"", + // 54 + "m\"月\"d\"日\"", + // 55 + "上午/下午h\"时\"mm\"分\"", + // 56 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 57 + "yyyy\"年\"m\"月\"", + // 58 + "m\"月\"d\"日\"", + // 59 + "t0", + // 60 + "t0.00", + // 61 + "t#,##0", + // 62 + "t#,##0.00", + // 63-66 No specific correspondence found in the official documentation. + // 63 + null, + // 64 + null, + // 65 + null, + // 66 + null, + // 67 + "t0%", + // 68 + "t0.00%", + // 69 + "t# ?/?", + // 70 + "t# ??/??", + // 71 + "ว/ด/ปปปป", + // 72 + "ว-ดดด-ปป", + // 73 + "ว-ดดด", + // 74 + "ดดด-ปป", + // 75 + "ช:นน", + // 76 + "ช:นน:ทท", + // 77 + "ว/ด/ปปปป ช:นน", + // 78 + "นน:ทท", + // 79 + "[ช]:นน:ทท", + // 80 + "นน:ทท.0", + // 81 + "d/m/bb", + // end + }; + + private static final String[] BUILTIN_FORMATS_US = { + // 0 + "General", + // 1 + "0", + // 2 + "0.00", + // 3 + "#,##0", + // 4 + "#,##0.00", + // 5 + "\"$\"#,##0_);(\"$\"#,##0)", + // 6 + "\"$\"#,##0_);[Red](\"$\"#,##0)", + // 7 + "\"$\"#,##0.00_);(\"$\"#,##0.00)", + // 8 + "\"$\"#,##0.00_);[Red](\"$\"#,##0.00)", + // 9 + "0%", + // 10 + "0.00%", + // 11 + "0.00E+00", + // 12 + "# ?/?", + // 13 + "# ??/??", + // 14 + // The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d". + "yyyy/m/d", + // 15 + "d-mmm-yy", + // 16 + "d-mmm", + // 17 + "mmm-yy", + // 18 + "h:mm AM/PM", + // 19 + "h:mm:ss AM/PM", + // 20 + "h:mm", + // 21 + "h:mm:ss", + // 22 + // The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm". + "yyyy-m-d h:mm", + // 23-26 No specific correspondence found in the official documentation. + // 23 + null, + // 24 + null, + // 25 + null, + // 26 + null, + // 27 + "yyyy\"年\"m\"月\"", + // 28 + "m\"月\"d\"日\"", + // 29 + "m\"月\"d\"日\"", + // 30 + "m-d-yy", + // 31 + "yyyy\"年\"m\"月\"d\"日\"", + // 32 + "h\"时\"mm\"分\"", + // 33 + "h\"时\"mm\"分\"ss\"秒\"", + // 34 + "上午/下午h\"时\"mm\"分\"", + // 35 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 36 + "yyyy\"年\"m\"月\"", + // 37 + "#,##0_);(#,##0)", + // 38 + "#,##0_);[Red](#,##0)", + // 39 + "#,##0.00_);(#,##0.00)", + // 40 + "#,##0.00_);[Red](#,##0.00)", + // 41 + "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + // 42 + "_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)", + // 43 + "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + // 44 + "_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)", + // 45 + "mm:ss", + // 46 + "[h]:mm:ss", + // 47 + "mm:ss.0", + // 48 + "##0.0E+0", + // 49 + "@", + // 50 + "yyyy\"年\"m\"月\"", + // 51 + "m\"月\"d\"日\"", + // 52 + "yyyy\"年\"m\"月\"", + // 53 + "m\"月\"d\"日\"", + // 54 + "m\"月\"d\"日\"", + // 55 + "上午/下午h\"时\"mm\"分\"", + // 56 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 57 + "yyyy\"年\"m\"月\"", + // 58 + "m\"月\"d\"日\"", + // 59 + "t0", + // 60 + "t0.00", + // 61 + "t#,##0", + // 62 + "t#,##0.00", + // 63-66 No specific correspondence found in the official documentation. + // 63 + null, + // 64 + null, + // 65 + null, + // 66 + null, + // 67 + "t0%", + // 68 + "t0.00%", + // 69 + "t# ?/?", + // 70 + "t# ??/??", + // 71 + "ว/ด/ปปปป", + // 72 + "ว-ดดด-ปป", + // 73 + "ว-ดดด", + // 74 + "ดดด-ปป", + // 75 + "ช:นน", + // 76 + "ช:นน:ทท", + // 77 + "ว/ด/ปปปป ช:นน", + // 78 + "นน:ทท", + // 79 + "[ช]:นน:ทท", + // 80 + "นน:ทท.0", + // 81 + "d/m/bb", + // end + }; + + public static String getBuiltinFormat(Integer index, String defaultFormat, Locale locale) { + String[] builtinFormat = switchBuiltinFormats(locale); + if (index == null || index < 0 || index >= builtinFormat.length) { + return defaultFormat; + } + return builtinFormat[index]; + } + + private static String[] switchBuiltinFormats(Locale locale) { + if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) { + return BUILTIN_FORMATS_US; + } + return BUILTIN_FORMATS_CN; + } + +} diff --git a/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java b/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java index f01a33b..38c2005 100644 --- a/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java +++ b/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java @@ -4,22 +4,54 @@ package com.alibaba.excel.constant; * @author jipengfei */ public class ExcelXmlConstants { - public static final String DIMENSION = "dimension"; - public static final String DIMENSION_REF = "ref"; - public static final String POSITION = "r"; - + public static final String DIMENSION_TAG = "dimension"; public static final String ROW_TAG = "row"; - public static final String CELL_TAG = "c"; - public static final String CELL_VALUE_TYPE_TAG = "t"; - /** - * Number formatted label - */ - public static final String CELL_DATA_FORMAT_TAG = "s"; public static final String CELL_FORMULA_TAG = "f"; public static final String CELL_VALUE_TAG = "v"; /** * When the data is "inlineStr" his tag is "t" */ public static final String CELL_INLINE_STRING_VALUE_TAG = "t"; + public static final String CELL_TAG = "c"; + public static final String MERGE_CELL_TAG = "mergeCell"; + public static final String HYPERLINK_TAG = "hyperlink"; + + public static final String X_DIMENSION_TAG = "x:dimension"; + public static final String X_ROW_TAG = "x:row"; + public static final String X_CELL_FORMULA_TAG = "x:f"; + public static final String X_CELL_VALUE_TAG = "x:v"; + /** + * When the data is "inlineStr" his tag is "t" + */ + public static final String X_CELL_INLINE_STRING_VALUE_TAG = "x:t"; + public static final String X_CELL_TAG = "x:c"; + public static final String X_MERGE_CELL_TAG = "x:mergeCell"; + public static final String X_HYPERLINK_TAG = "x:hyperlink"; + + /** + * s attribute + */ + public static final String ATTRIBUTE_S = "s"; + /** + * ref attribute + */ + public static final String ATTRIBUTE_REF = "ref"; + /** + * r attribute + */ + public static final String ATTRIBUTE_R = "r"; + /** + * t attribute + */ + public static final String ATTRIBUTE_T = "t"; + /** + * location attribute + */ + public static final String ATTRIBUTE_LOCATION = "location"; + + /** + * Cell range split + */ + public static final String CELL_RANGE_SPLIT = ":"; } diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java index f44c2b3..df17f55 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -1,8 +1,8 @@ package com.alibaba.excel.context; import java.io.InputStream; +import java.util.List; -import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -10,6 +10,7 @@ import com.alibaba.excel.read.metadata.holder.ReadHolder; import com.alibaba.excel.read.metadata.holder.ReadRowHolder; import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.processor.AnalysisEventProcessor; import com.alibaba.excel.support.ExcelTypeEnum; /** @@ -70,6 +71,27 @@ public interface AnalysisContext { */ Object getCustom(); + /** + * Event processor + * + * @return + */ + AnalysisEventProcessor analysisEventProcessor(); + + /** + * Data that the customer needs to read + * + * @return + */ + List readSheetList(); + + /** + * Data that the customer needs to read + * + * @param readSheetList + */ + void readSheetList(List readSheetList); + /** * get current sheet * diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java index e5ded50..ab27e87 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -1,6 +1,7 @@ package com.alibaba.excel.context; import java.io.InputStream; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +14,12 @@ import com.alibaba.excel.read.metadata.holder.ReadHolder; import com.alibaba.excel.read.metadata.holder.ReadRowHolder; import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.read.processor.AnalysisEventProcessor; +import com.alibaba.excel.read.processor.DefaultAnalysisEventProcessor; import com.alibaba.excel.support.ExcelTypeEnum; /** @@ -37,13 +44,27 @@ public class AnalysisContextImpl implements AnalysisContext { * Configuration of currently operated cell */ private ReadHolder currentReadHolder; + /** + * Event processor + */ + private AnalysisEventProcessor analysisEventProcessor; - public AnalysisContextImpl(ReadWorkbook readWorkbook) { + public AnalysisContextImpl(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { if (readWorkbook == null) { throw new IllegalArgumentException("Workbook argument cannot be null"); } - readWorkbookHolder = new ReadWorkbookHolder(readWorkbook); + switch (actualExcelType) { + case XLS: + readWorkbookHolder = new XlsReadWorkbookHolder(readWorkbook); + break; + case XLSX: + readWorkbookHolder = new XlsxReadWorkbookHolder(readWorkbook); + break; + default: + break; + } currentReadHolder = readWorkbookHolder; + analysisEventProcessor = new DefaultAnalysisEventProcessor(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Initialization 'AnalysisContextImpl' complete"); } @@ -51,7 +72,16 @@ public class AnalysisContextImpl implements AnalysisContext { @Override public void currentSheet(ReadSheet readSheet) { - readSheetHolder = new ReadSheetHolder(readSheet, readWorkbookHolder); + switch (readWorkbookHolder.getExcelType()) { + case XLS: + readSheetHolder = new XlsReadSheetHolder(readSheet, readWorkbookHolder); + break; + case XLSX: + readSheetHolder = new XlsxReadSheetHolder(readSheet, readWorkbookHolder); + break; + default: + break; + } currentReadHolder = readSheetHolder; if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { throw new ExcelAnalysisException("Cannot read sheet repeatedly."); @@ -92,6 +122,21 @@ public class AnalysisContextImpl implements AnalysisContext { return readWorkbookHolder.getCustomObject(); } + @Override + public AnalysisEventProcessor analysisEventProcessor() { + return analysisEventProcessor; + } + + @Override + public List readSheetList() { + return null; + } + + @Override + public void readSheetList(List readSheetList) { + + } + @Override public Sheet getCurrentSheet() { Sheet sheet = new Sheet(readSheetHolder.getSheetNo() + 1); diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index e79453c..8465cbd 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -27,7 +27,9 @@ import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.util.WriteHandlerUtils; @@ -307,7 +309,6 @@ public class WriteContextImpl implements WriteContext { throwable = t; } } - if (!isOutputStreamEncrypt) { try { if (writeExcel) { @@ -318,7 +319,6 @@ public class WriteContextImpl implements WriteContext { throwable = t; } } - try { Workbook workbook = writeWorkbookHolder.getWorkbook(); if (workbook instanceof SXSSFWorkbook) { @@ -327,7 +327,6 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwable = t; } - try { if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { writeWorkbookHolder.getOutputStream().close(); @@ -335,7 +334,6 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwable = t; } - if (writeExcel && !isOutputStreamEncrypt) { try { doFileEncrypt07(); @@ -343,7 +341,6 @@ public class WriteContextImpl implements WriteContext { throwable = t; } } - try { if (writeWorkbookHolder.getTempTemplateInputStream() != null) { writeWorkbookHolder.getTempTemplateInputStream().close(); @@ -351,18 +348,21 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwable = t; } - clearEncrypt03(); - + removeThreadLocalCache(); if (throwable != null) { throw new ExcelGenerateException("Can not close IO.", throwable); } - if (LOGGER.isDebugEnabled()) { LOGGER.debug("Finished write."); } } + private void removeThreadLocalCache() { + NumberDataFormatterUtils.removeThreadLocalCache(); + DateUtils.removeThreadLocalCache(); + } + @Override public Sheet getCurrentSheet() { return writeSheetHolder.getSheet(); diff --git a/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java b/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java new file mode 100644 index 0000000..698bf6a --- /dev/null +++ b/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.context.xls; + +import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + */ +public class DefaultXlsReadContext extends AnalysisContextImpl implements XlsReadContext { + + public DefaultXlsReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + super(readWorkbook, actualExcelType); + } + + @Override + public XlsReadWorkbookHolder xlsReadWorkbookHolder() { + return (XlsReadWorkbookHolder)readWorkbookHolder(); + } + + @Override + public XlsReadSheetHolder xlsReadSheetHolder() { + return (XlsReadSheetHolder)readSheetHolder(); + } +} diff --git a/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java b/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java new file mode 100644 index 0000000..4d1bcdb --- /dev/null +++ b/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.context.xls; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; + +/** + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + **/ +public interface XlsReadContext extends AnalysisContext { + /** + * All information about the workbook you are currently working on. + * + * @return Current workbook holder + */ + XlsReadWorkbookHolder xlsReadWorkbookHolder(); + + /** + * All information about the sheet you are currently working on. + * + * @return Current sheet holder + */ + XlsReadSheetHolder xlsReadSheetHolder(); +} diff --git a/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java b/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java new file mode 100644 index 0000000..5c9d638 --- /dev/null +++ b/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.context.xlsx; + +import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + */ +public class DefaultXlsxReadContext extends AnalysisContextImpl implements XlsxReadContext { + + public DefaultXlsxReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + super(readWorkbook, actualExcelType); + } + + @Override + public XlsxReadWorkbookHolder xlsxReadWorkbookHolder() { + return (XlsxReadWorkbookHolder)readWorkbookHolder(); + } + + @Override + public XlsxReadSheetHolder xlsxReadSheetHolder() { + return (XlsxReadSheetHolder)readSheetHolder(); + } +} diff --git a/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java b/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java new file mode 100644 index 0000000..58d8123 --- /dev/null +++ b/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.context.xlsx; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; + +/** + * A context is the main anchorage point of a ls xlsx reader. + * + * @author Jiaju Zhuang + **/ +public interface XlsxReadContext extends AnalysisContext { + /** + * All information about the workbook you are currently working on. + * + * @return Current workbook holder + */ + XlsxReadWorkbookHolder xlsxReadWorkbookHolder(); + + /** + * All information about the sheet you are currently working on. + * + * @return Current sheet holder + */ + XlsxReadSheetHolder xlsxReadSheetHolder(); +} diff --git a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java index 77e8b59..0182255 100644 --- a/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java +++ b/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java @@ -48,56 +48,12 @@ public class DefaultConverterLoader { private static Map defaultWriteConverter; private static Map allConverter; - /** - * Load default write converter - * - * @return - */ - public static Map loadDefaultWriteConverter() { - if (defaultWriteConverter != null) { - return defaultWriteConverter; - } - defaultWriteConverter = new HashMap(32); - putWriteConverter(new BigDecimalNumberConverter()); - putWriteConverter(new BooleanBooleanConverter()); - putWriteConverter(new ByteNumberConverter()); - putWriteConverter(new DateStringConverter()); - putWriteConverter(new DoubleNumberConverter()); - putWriteConverter(new FloatNumberConverter()); - putWriteConverter(new IntegerNumberConverter()); - putWriteConverter(new LongNumberConverter()); - putWriteConverter(new ShortNumberConverter()); - putWriteConverter(new StringStringConverter()); - putWriteConverter(new FileImageConverter()); - putWriteConverter(new InputStreamImageConverter()); - putWriteConverter(new ByteArrayImageConverter()); - putWriteConverter(new BoxingByteArrayImageConverter()); - putWriteConverter(new UrlImageConverter()); - return defaultWriteConverter; + static { + initDefaultWriteConverter(); + initAllConverter(); } - private static void putWriteConverter(Converter converter) { - defaultWriteConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); - } - - /** - * Load default read converter - * - * @return - */ - public static Map loadDefaultReadConverter() { - return loadAllConverter(); - } - - /** - * Load all converter - * - * @return - */ - public static Map loadAllConverter() { - if (allConverter != null) { - return allConverter; - } + private static void initAllConverter() { allConverter = new HashMap(64); putAllConverter(new BigDecimalBooleanConverter()); putAllConverter(new BigDecimalNumberConverter()); @@ -138,6 +94,55 @@ public class DefaultConverterLoader { putAllConverter(new StringNumberConverter()); putAllConverter(new StringStringConverter()); putAllConverter(new StringErrorConverter()); + } + + private static void initDefaultWriteConverter() { + defaultWriteConverter = new HashMap(32); + putWriteConverter(new BigDecimalNumberConverter()); + putWriteConverter(new BooleanBooleanConverter()); + putWriteConverter(new ByteNumberConverter()); + putWriteConverter(new DateStringConverter()); + putWriteConverter(new DoubleNumberConverter()); + putWriteConverter(new FloatNumberConverter()); + putWriteConverter(new IntegerNumberConverter()); + putWriteConverter(new LongNumberConverter()); + putWriteConverter(new ShortNumberConverter()); + putWriteConverter(new StringStringConverter()); + putWriteConverter(new FileImageConverter()); + putWriteConverter(new InputStreamImageConverter()); + putWriteConverter(new ByteArrayImageConverter()); + putWriteConverter(new BoxingByteArrayImageConverter()); + putWriteConverter(new UrlImageConverter()); + } + + /** + * Load default write converter + * + * @return + */ + public static Map loadDefaultWriteConverter() { + return defaultWriteConverter; + } + + private static void putWriteConverter(Converter converter) { + defaultWriteConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); + } + + /** + * Load default read converter + * + * @return + */ + public static Map loadDefaultReadConverter() { + return loadAllConverter(); + } + + /** + * Load all converter + * + * @return + */ + public static Map loadAllConverter() { return allConverter; } diff --git a/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java b/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java index 8c16a77..f1facdf 100644 --- a/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java @@ -34,7 +34,7 @@ public class ByteNumberConverter implements Converter { @Override public CellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(BigDecimal.valueOf(value)); + return new CellData(new BigDecimal(Byte.toString(value))); } } diff --git a/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java b/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java index a1b6471..69cc22e 100644 --- a/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java @@ -34,7 +34,7 @@ public class FloatNumberConverter implements Converter { @Override public CellData convertToExcelData(Float value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(BigDecimal.valueOf(value)); + return new CellData(new BigDecimal(Float.toString(value))); } } diff --git a/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java b/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java index 3b0deac..117d4c1 100644 --- a/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java @@ -34,7 +34,7 @@ public class IntegerNumberConverter implements Converter { @Override public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(BigDecimal.valueOf(value)); + return new CellData(new BigDecimal(Integer.toString(value))); } } diff --git a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java index 7d1d7da..357c6ae 100644 --- a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java @@ -34,7 +34,7 @@ public class ShortNumberConverter implements Converter { @Override public CellData convertToExcelData(Short value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(BigDecimal.valueOf(value)); + return new CellData(new BigDecimal(Short.toString(value))); } } diff --git a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java index f536a08..b31bc70 100644 --- a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java @@ -10,7 +10,9 @@ import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; import com.alibaba.excel.util.NumberUtils; +import com.alibaba.excel.util.StringUtils; /** * String and number converter @@ -44,13 +46,9 @@ public class StringNumberConverter implements Converter { return NumberUtils.format(cellData.getNumberValue(), contentProperty); } // Excel defines formatting - if (cellData.getDataFormat() != null) { - if (DateUtil.isADateFormat(cellData.getDataFormat(), cellData.getDataFormatString())) { - return DateUtils.format(DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), - globalConfiguration.getUse1904windowing(), null)); - } else { - return NumberUtils.format(cellData.getNumberValue(), contentProperty); - } + if (cellData.getDataFormat() != null && !StringUtils.isEmpty(cellData.getDataFormatString())) { + return NumberDataFormatterUtils.format(cellData.getNumberValue().doubleValue(), cellData.getDataFormat(), + cellData.getDataFormatString(), globalConfiguration); } // Default conversion number return NumberUtils.format(cellData.getNumberValue(), contentProperty); diff --git a/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java b/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java new file mode 100644 index 0000000..1c13b7c --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.enums; + +/** + * Extra data type + * + * @author Jiaju Zhuang + **/ +public enum CellExtraTypeEnum { + /** + * Comment + */ + COMMENT, + /** + * Hyperlink + */ + HYPERLINK, + /** + * Merge + */ + MERGE,; +} diff --git a/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java b/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java new file mode 100644 index 0000000..e371cbd --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * The types of row + * + * @author Jiaju Zhuang + **/ +public enum RowTypeEnum { + /** + * data + */ + DATA, + /** + * empty + */ + EMPTY,; +} diff --git a/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java b/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java index 22a95f0..1e93397 100644 --- a/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java +++ b/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java @@ -1,6 +1,7 @@ package com.alibaba.excel.event; import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.CellExtra; import com.alibaba.excel.read.listener.ReadListener; /** @@ -20,6 +21,17 @@ public abstract class AbstractIgnoreExceptionReadListener implements ReadList @Override public void onException(Exception exception, AnalysisContext context) {} + /** + * The current method is called when extra information is returned + * + * @param extra + * extra information + * @param context + * analysis context + */ + @Override + public void extra(CellExtra extra, AnalysisContext context) {} + @Override public boolean hasNext(AnalysisContext context) { return true; diff --git a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java b/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java index 176bd79..4b1b802 100644 --- a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java +++ b/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java @@ -4,6 +4,7 @@ import java.util.Map; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.CellExtra; import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.util.ConverterUtils; @@ -27,6 +28,17 @@ public abstract class AnalysisEventListener implements ReadListener { */ public void invokeHeadMap(Map headMap, AnalysisContext context) {} + /** + * The current method is called when extra information is returned + * + * @param extra + * extra information + * @param context + * analysis context + */ + @Override + public void extra(CellExtra extra, AnalysisContext context) {} + /** * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the * entire read will terminate. diff --git a/src/main/java/com/alibaba/excel/metadata/AbstractCell.java b/src/main/java/com/alibaba/excel/metadata/AbstractCell.java new file mode 100644 index 0000000..5cea37c --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/AbstractCell.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.metadata; + +/** + * cell + * + * @author Jiaju Zhuang + **/ +public class AbstractCell implements Cell { + /** + * Row index + */ + private Integer rowIndex; + /** + * Column index + */ + private Integer columnIndex; + + public Integer getRowIndex() { + return rowIndex; + } + + public void setRowIndex(Integer rowIndex) { + this.rowIndex = rowIndex; + } + + public Integer getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(Integer columnIndex) { + this.columnIndex = columnIndex; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java b/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java index 37a1942..6bfd825 100644 --- a/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java +++ b/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java @@ -1,6 +1,7 @@ package com.alibaba.excel.metadata; import java.util.List; +import java.util.Locale; import java.util.Map; import com.alibaba.excel.converters.Converter; @@ -27,7 +28,6 @@ public abstract class AbstractHolder implements ConfigurationHolder { * Some global variables */ private GlobalConfiguration globalConfiguration; - /** *

* Read key: @@ -58,6 +58,16 @@ public abstract class AbstractHolder implements ConfigurationHolder { } else { globalConfiguration.setAutoTrim(basicParameter.getAutoTrim()); } + + if (basicParameter.getLocale() == null) { + if (prentAbstractHolder == null) { + globalConfiguration.setLocale(Locale.getDefault()); + } else { + globalConfiguration.setLocale(prentAbstractHolder.getGlobalConfiguration().getLocale()); + } + } else { + globalConfiguration.setLocale(basicParameter.getLocale()); + } } public Boolean getNewInitialization() { diff --git a/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java b/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java new file mode 100644 index 0000000..66df25b --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java @@ -0,0 +1,98 @@ +package com.alibaba.excel.metadata; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import com.alibaba.excel.converters.Converter; + +/** + * ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractParameterBuilder { + /** + * You can only choose one of the {@link #head(List)} and {@link #head(Class)} + * + * @param head + * @return + */ + public T head(List> head) { + parameter().setHead(head); + return self(); + } + + /** + * You can only choose one of the {@link #head(List)} and {@link #head(Class)} + * + * @param clazz + * @return + */ + public T head(Class clazz) { + parameter().setClazz(clazz); + return self(); + } + + /** + * Custom type conversions override the default. + * + * @param converter + * @return + */ + public T registerConverter(Converter converter) { + if (parameter().getCustomConverterList() == null) { + parameter().setCustomConverterList(new ArrayList()); + } + parameter().getCustomConverterList().add(converter); + return self(); + } + + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + * + * default is false + * + * @param use1904windowing + * @return + */ + public T use1904windowing(Boolean use1904windowing) { + parameter().setUse1904windowing(use1904windowing); + return self(); + } + + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + * + * @param locale + * @return + */ + public T locale(Locale locale) { + parameter().setLocale(locale); + return self(); + } + + /** + * Automatic trim includes sheet name and content + * + * @param autoTrim + * @return + */ + public T autoTrim(Boolean autoTrim) { + parameter().setAutoTrim(autoTrim); + return self(); + } + + @SuppressWarnings("unchecked") + protected T self() { + return (T)this; + } + + /** + * Get parameter + * + * @return + */ + protected abstract C parameter(); +} diff --git a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java index 40bffe6..d00c93a 100644 --- a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java +++ b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java @@ -1,6 +1,7 @@ package com.alibaba.excel.metadata; import java.util.List; +import java.util.Locale; import com.alibaba.excel.converters.Converter; @@ -34,6 +35,11 @@ public class BasicParameter { * @return */ private Boolean use1904windowing; + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + */ + private Locale locale; public List> getHead() { return head; @@ -75,4 +81,11 @@ public class BasicParameter { this.use1904windowing = use1904windowing; } + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/Cell.java b/src/main/java/com/alibaba/excel/metadata/Cell.java new file mode 100644 index 0000000..259df7a --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/Cell.java @@ -0,0 +1,22 @@ +package com.alibaba.excel.metadata; + +/** + * Cell + * + * @author Jiaju Zhuang + **/ +public interface Cell { + /** + * Row index + * + * @return + */ + Integer getRowIndex(); + + /** + * Column index + * + * @return + */ + Integer getColumnIndex(); +} diff --git a/src/main/java/com/alibaba/excel/metadata/CellData.java b/src/main/java/com/alibaba/excel/metadata/CellData.java index 7cd5496..2124e10 100644 --- a/src/main/java/com/alibaba/excel/metadata/CellData.java +++ b/src/main/java/com/alibaba/excel/metadata/CellData.java @@ -12,7 +12,7 @@ import com.alibaba.excel.util.StringUtils; * * @author Jiaju Zhuang */ -public class CellData { +public class CellData extends AbstractCell { private CellDataTypeEnum type; /** * {@link CellDataTypeEnum#NUMBER} @@ -226,6 +226,42 @@ public class CellData { } } + public static CellData newEmptyInstance() { + return newEmptyInstance(null, null); + } + + public static CellData newEmptyInstance(Integer rowIndex, Integer columnIndex) { + CellData cellData = new CellData(CellDataTypeEnum.EMPTY); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static CellData newInstance(Boolean booleanValue) { + return newInstance(booleanValue, null, null); + } + + public static CellData newInstance(Boolean booleanValue, Integer rowIndex, Integer columnIndex) { + CellData cellData = new CellData(booleanValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static CellData newInstance(String stringValue, Integer rowIndex, Integer columnIndex) { + CellData cellData = new CellData(stringValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static CellData newInstance(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) { + CellData cellData = new CellData(numberValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + @Override public String toString() { if (type == null) { diff --git a/src/main/java/com/alibaba/excel/metadata/CellExtra.java b/src/main/java/com/alibaba/excel/metadata/CellExtra.java new file mode 100644 index 0000000..937b9ac --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/CellExtra.java @@ -0,0 +1,121 @@ +package com.alibaba.excel.metadata; + +import org.apache.poi.ss.util.CellReference; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.enums.CellExtraTypeEnum; + +/** + * Cell extra information. + * + * @author Jiaju Zhuang + */ +public class CellExtra extends AbstractCell { + /** + * Cell extra type + */ + private CellExtraTypeEnum type; + /** + * Cell extra data + */ + private String text; + /** + * First row index,if this object is an interval + */ + private Integer firstRowIndex; + /** + * Last row index,if this object is an interval + */ + private Integer lastRowIndex; + /** + * First column index,if this object is an interval + */ + private Integer firstColumnIndex; + /** + * Last column index,if this object is an interval + */ + private Integer lastColumnIndex; + + public CellExtra(CellExtraTypeEnum type, String text, String range) { + super(); + this.type = type; + this.text = text; + String[] ranges = range.split(ExcelXmlConstants.CELL_RANGE_SPLIT); + CellReference first = new CellReference(ranges[0]); + CellReference last = first; + this.firstRowIndex = first.getRow(); + this.firstColumnIndex = (int)first.getCol(); + setRowIndex(this.firstRowIndex); + setColumnIndex(this.firstColumnIndex); + if (ranges.length > 1) { + last = new CellReference(ranges[1]); + } + this.lastRowIndex = last.getRow(); + this.lastColumnIndex = (int)last.getCol(); + } + + public CellExtra(CellExtraTypeEnum type, String text, Integer rowIndex, Integer columnIndex) { + this(type, text, rowIndex, rowIndex, columnIndex, columnIndex); + } + + public CellExtra(CellExtraTypeEnum type, String text, Integer firstRowIndex, Integer lastRowIndex, + Integer firstColumnIndex, Integer lastColumnIndex) { + super(); + setRowIndex(firstRowIndex); + setColumnIndex(firstColumnIndex); + this.type = type; + this.text = text; + this.firstRowIndex = firstRowIndex; + this.firstColumnIndex = firstColumnIndex; + this.lastRowIndex = lastRowIndex; + this.lastColumnIndex = lastColumnIndex; + } + + public CellExtraTypeEnum getType() { + return type; + } + + public void setType(CellExtraTypeEnum type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Integer getFirstRowIndex() { + return firstRowIndex; + } + + public void setFirstRowIndex(Integer firstRowIndex) { + this.firstRowIndex = firstRowIndex; + } + + public Integer getFirstColumnIndex() { + return firstColumnIndex; + } + + public void setFirstColumnIndex(Integer firstColumnIndex) { + this.firstColumnIndex = firstColumnIndex; + } + + public Integer getLastRowIndex() { + return lastRowIndex; + } + + public void setLastRowIndex(Integer lastRowIndex) { + this.lastRowIndex = lastRowIndex; + } + + public Integer getLastColumnIndex() { + return lastColumnIndex; + } + + public void setLastColumnIndex(Integer lastColumnIndex) { + this.lastColumnIndex = lastColumnIndex; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/DataFormatter.java b/src/main/java/com/alibaba/excel/metadata/DataFormatter.java new file mode 100644 index 0000000..7467cd2 --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/DataFormatter.java @@ -0,0 +1,786 @@ +/* + * ==================================================================== Licensed to the Apache Software Foundation (ASF) + * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff + * can be found in svn at location root/projects/3rd-party/src + * ==================================================================== + */ +package com.alibaba.excel.metadata; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DateFormatSymbols; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat; +import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; +import org.apache.poi.ss.usermodel.FractionFormat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.util.DateUtils; + +/** + * Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date + * conversion. + *

+ * This is a non-thread-safe class. + * + * @author Jiaju Zhuang + */ +public class DataFormatter { + /** 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 "#" */ + private static final Pattern numPattern = Pattern.compile("[0#]+"); + + /** 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 */ + private static final Pattern amPmPattern = + Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE); + + /** Pattern to find formats with condition ranges e.g. [>=100] */ + private static final Pattern rangeConditionalPattern = + Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); + + /** + * A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into + * locales + */ + private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])"); + + /** + * A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red, + * White, Yellow, "Color n" (1<=n<=56) + */ + private static final Pattern colorPattern = Pattern.compile( + "(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" + "(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|" + + "(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])|(\\[DBNum(1|2|3)])|(\\[\\$-\\d{0,3}])", + Pattern.CASE_INSENSITIVE); + + /** + * A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called + */ + private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)"); + + /** + * A regex to strip junk out of fraction formats + */ + private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ ?#\\d/]+)"); + + /** + * A regex to detect if an alternate grouping character is used in a numeric format + */ + private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})"); + + /** + * Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs + * ("#"). + */ + private static final String invalidDateTimeString; + static { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 255; i++) + buf.append('#'); + invalidDateTimeString = buf.toString(); + } + + /** + * The decimal symbols of the locale used for formatting values. + */ + private DecimalFormatSymbols decimalSymbols; + + /** + * 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. */ + private Format defaultNumFormat; + /** + * A map to cache formats. Map formats + */ + private final Map formats = new HashMap(); + + /** 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. + * + * default is false + * + * @return + */ + private Boolean use1904windowing; + + /** + * Creates a formatter using the {@link Locale#getDefault() default locale}. + */ + public DataFormatter() { + this(null, null); + } + + /** + * Creates a formatter using the given locale. + * + */ + public DataFormatter(Locale locale, Boolean use1904windowing) { + this.use1904windowing = use1904windowing != null ? use1904windowing : Boolean.FALSE; + this.locale = locale != null ? locale : Locale.getDefault(); + this.dateSymbols = DateFormatSymbols.getInstance(this.locale); + this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale); + } + + private Format getFormat(Integer dataFormat, String dataFormatString) { + // See if we already have it cached + Format format = formats.get(dataFormatString); + if (format != null) { + return format; + } + // Is it one of the special built in types, General or @? + if ("General".equalsIgnoreCase(dataFormatString) || "@".equals(dataFormatString)) { + format = getDefaultFormat(); + addFormat(dataFormatString, format); + return format; + } + + // Build a formatter, and cache it + format = createFormat(dataFormat, dataFormatString); + addFormat(dataFormatString, format); + return format; + } + + private Format createFormat(Integer dataFormat, String dataFormatString) { + String formatStr = dataFormatString; + + Format format = checkSpecialConverter(formatStr); + if (format != null) { + return format; + } + + // Remove colour formatting if present + Matcher colourM = colorPattern.matcher(formatStr); + while (colourM.find()) { + String colour = colourM.group(); + + // Paranoid replacement... + int at = formatStr.indexOf(colour); + if (at == -1) + break; + String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); + if (nFormatStr.equals(formatStr)) + break; + + // Try again in case there's multiple + formatStr = nFormatStr; + colourM = colorPattern.matcher(formatStr); + } + + // Strip off the locale information, we use an instance-wide locale for everything + Matcher m = localePatternGroup.matcher(formatStr); + while (m.find()) { + String match = m.group(); + String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-')); + if (symbol.indexOf('$') > -1) { + symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + symbol.substring(symbol.indexOf('$')); + } + formatStr = m.replaceAll(symbol); + m = localePatternGroup.matcher(formatStr); + } + + // Check for special cases + if (formatStr == null || formatStr.trim().length() == 0) { + return getDefaultFormat(); + } + + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return getDefaultFormat(); + } + + if (DateUtils.isADateFormat(dataFormat, formatStr)) { + return createDateFormat(formatStr); + } + // Excel supports fractions in format strings, which Java doesn't + if (formatStr.contains("#/") || formatStr.contains("?/")) { + String[] chunks = formatStr.split(";"); + for (String chunk1 : chunks) { + String chunk = chunk1.replaceAll("\\?", "#"); + Matcher matcher = fractionStripper.matcher(chunk); + chunk = matcher.replaceAll(" "); + chunk = chunk.replaceAll(" +", " "); + Matcher fractionMatcher = fractionPattern.matcher(chunk); + // take the first match + if (fractionMatcher.find()) { + String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat; + return new FractionFormat(wholePart, fractionMatcher.group(3)); + } + } + + // Strip custom text in quotes and escaped characters for now as it can cause performance problems in + // fractions. + // String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", + // "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#"); + return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat); + } + + if (numPattern.matcher(formatStr).find()) { + return createNumberFormat(formatStr); + } + return getDefaultFormat(); + } + + private Format checkSpecialConverter(String dataFormatString) { + if ("00000\\-0000".equals(dataFormatString) || "00000-0000".equals(dataFormatString)) { + return new ZipPlusFourFormat(); + } + if ("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString) + || "[<=9999999]###-####;(###) ###-####".equals(dataFormatString) + || "###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString) + || "###-####;(###) ###-####".equals(dataFormatString)) { + return new PhoneFormat(); + } + if ("000\\-00\\-0000".equals(dataFormatString) || "000-00-0000".equals(dataFormatString)) { + return new SSNFormat(); + } + return null; + } + + private Format createDateFormat(String pFormatStr) { + String formatStr = pFormatStr; + formatStr = formatStr.replaceAll("\\\\-", "-"); + formatStr = formatStr.replaceAll("\\\\,", ","); + formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char + formatStr = formatStr.replaceAll("\\\\ ", " "); + formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy + formatStr = formatStr.replaceAll(";@", ""); + formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy + formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting + formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates + formatStr = formatStr.replace("\"", ""); + + boolean hasAmPm = false; + Matcher amPmMatcher = amPmPattern.matcher(formatStr); + while (amPmMatcher.find()) { + formatStr = amPmMatcher.replaceAll("@"); + hasAmPm = true; + amPmMatcher = amPmPattern.matcher(formatStr); + } + formatStr = formatStr.replaceAll("@", "a"); + + Matcher dateMatcher = daysAsText.matcher(formatStr); + if (dateMatcher.find()) { + String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E"); + formatStr = dateMatcher.replaceAll(match); + } + + // Convert excel date format to SimpleDateFormat. + // Excel uses lower and upper case 'm' for both minutes and months. + // From Excel help: + /* + The "m" or "mm" code must appear immediately after the "h" or"hh" + code or immediately before the "ss" code; otherwise, Microsoft + Excel displays the month instead of minutes." + */ + StringBuilder sb = new StringBuilder(); + char[] chars = formatStr.toCharArray(); + boolean mIsMonth = true; + List ms = new ArrayList(); + boolean isElapsed = false; + for (int j = 0; j < chars.length; j++) { + char c = chars[j]; + if (c == '\'') { + sb.append(c); + j++; + + // skip until the next quote + while (j < chars.length) { + c = chars[j]; + sb.append(c); + if (c == '\'') { + break; + } + j++; + } + } else if (c == '[' && !isElapsed) { + isElapsed = true; + mIsMonth = false; + sb.append(c); + } else if (c == ']' && isElapsed) { + isElapsed = false; + sb.append(c); + } else if (isElapsed) { + if (c == 'h' || c == 'H') { + sb.append('H'); + } else if (c == 'm' || c == 'M') { + sb.append('m'); + } else if (c == 's' || c == 'S') { + sb.append('s'); + } else { + sb.append(c); + } + } else if (c == 'h' || c == 'H') { + mIsMonth = false; + if (hasAmPm) { + sb.append('h'); + } else { + sb.append('H'); + } + } else if (c == 'm' || c == 'M') { + if (mIsMonth) { + sb.append('M'); + ms.add(Integer.valueOf(sb.length() - 1)); + } else { + sb.append('m'); + } + } else if (c == 's' || c == 'S') { + sb.append('s'); + // if 'M' precedes 's' it should be minutes ('m') + for (int index : ms) { + if (sb.charAt(index) == 'M') { + sb.replace(index, index + 1, "m"); + } + } + mIsMonth = true; + ms.clear(); + } else if (Character.isLetter(c)) { + mIsMonth = true; + ms.clear(); + if (c == 'y' || c == 'Y') { + sb.append('y'); + } else if (c == 'd' || c == 'D') { + sb.append('d'); + } else { + sb.append(c); + } + } else { + if (Character.isWhitespace(c)) { + ms.clear(); + } + sb.append(c); + } + } + formatStr = sb.toString(); + + try { + return new ExcelStyleDateFormatter(formatStr, dateSymbols); + } catch (IllegalArgumentException iae) { + LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(); + } + + } + + private String cleanFormatForNumber(String formatStr) { + StringBuilder sb = new StringBuilder(formatStr); + // If they requested spacers, with "_", + // remove those as we don't do spacing + // If they requested full-column-width + // padding, with "*", remove those too + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + sb.deleteCharAt(i + 1); + } + // Remove the _ too + sb.deleteCharAt(i); + i--; + } + } + + // Now, handle the other aspects like + // quoting and scientific notation + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + // remove quotes and back slashes + if (c == '\\' || c == '"') { + sb.deleteCharAt(i); + i--; + + // for scientific/engineering notation + } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') { + sb.deleteCharAt(i); + i--; + } + } + + return sb.toString(); + } + + private static class InternalDecimalFormatWithScale extends Format { + + private static final Pattern endsWithCommas = Pattern.compile("(,+)$"); + private BigDecimal divider; + private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000); + private final DecimalFormat df; + + private static String trimTrailingCommas(String s) { + return s.replaceAll(",+$", ""); + } + + public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) { + df = new DecimalFormat(trimTrailingCommas(pattern), symbols); + setExcelStyleRoundingMode(df); + Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern); + if (endsWithCommasMatcher.find()) { + String commas = (endsWithCommasMatcher.group(1)); + BigDecimal temp = BigDecimal.ONE; + for (int i = 0; i < commas.length(); ++i) { + temp = temp.multiply(ONE_THOUSAND); + } + divider = temp; + } else { + divider = null; + } + } + + private Object scaleInput(Object obj) { + if (divider != null) { + if (obj instanceof BigDecimal) { + obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP); + } else if (obj instanceof Double) { + obj = (Double)obj / divider.doubleValue(); + } else { + throw new UnsupportedOperationException(); + } + } + return obj; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + obj = scaleInput(obj); + return df.format(obj, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + } + + private Format createNumberFormat(String formatStr) { + String format = cleanFormatForNumber(formatStr); + DecimalFormatSymbols symbols = decimalSymbols; + + // Do we need to change the grouping character? + // eg for a format like #'##0 which wants 12'345 not 12,345 + Matcher agm = alternateGrouping.matcher(format); + if (agm.find()) { + char grouping = agm.group(2).charAt(0); + // Only replace the grouping character if it is not the default + // grouping character for the US locale (',') in order to enable + // correct grouping for non-US locales. + if (grouping != ',') { + symbols = DecimalFormatSymbols.getInstance(locale); + + symbols.setGroupingSeparator(grouping); + String oldPart = agm.group(1); + String newPart = oldPart.replace(grouping, ','); + format = format.replace(oldPart, newPart); + } + } + + try { + return new InternalDecimalFormatWithScale(format, symbols); + } catch (IllegalArgumentException iae) { + LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(); + } + } + + private Format getDefaultFormat() { + // for numeric cells try user supplied default + if (defaultNumFormat != null) { + return defaultNumFormat; + // otherwise use general format + } + defaultNumFormat = new ExcelGeneralNumberFormat(locale); + return defaultNumFormat; + } + + /** + * Performs Excel-style date formatting, using the supplied Date and format + */ + private String performDateFormatting(Date d, Format dateFormat) { + Format df = dateFormat != null ? dateFormat : getDefaultFormat(); + return df.format(d); + } + + /** + * Returns the formatted value of an Excel date as a String based on the cell's DataFormat. + * i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc. + *

+ * If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a + * 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 dataFormat + * @param dataFormatString + * @return Formatted value + */ + private String getFormattedDateString(Double data, Integer dataFormat, String dataFormatString) { + Format dateFormat = getFormat(dataFormat, dataFormatString); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data); + } + return performDateFormatting(DateUtil.getJavaDate(data, use1904windowing), dateFormat); + } + + /** + * Returns the formatted value of an Excel number as a String based on the cell's DataFormat. + * Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800) + * 555-1234". + *

+ * Format comes from either the highest priority conditional format rule with a specified format, or from the cell + * style. + * + * @param data + * to format + * @param dataFormat + * @param dataFormatString + * @return a formatted number string + */ + private String getFormattedNumberString(Double data, Integer dataFormat, String dataFormatString) { + Format numberFormat = getFormat(dataFormat, dataFormatString); + String formatted = numberFormat.format(data); + return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation + } + + /** + * Format data. + * + * @param data + * @param dataFormat + * @param dataFormatString + * @return + */ + public String format(Double data, Integer dataFormat, String dataFormatString) { + if (DateUtils.isADateFormat(dataFormat, dataFormatString)) { + return getFormattedDateString(data, dataFormat, dataFormatString); + } + return getFormattedNumberString(data, dataFormat, dataFormatString); + } + + /** + *

+ * Sets a default number format to be used when the Excel format cannot be parsed successfully. Note: This is + * a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells + * with the General format. + *

+ *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param format + * A Format instance to be used as a default + * @see Format#format + */ + public void setDefaultNumberFormat(Format format) { + for (Map.Entry entry : formats.entrySet()) { + if (entry.getValue() == defaultNumFormat) { + entry.setValue(format); + } + } + defaultNumFormat = format; + } + + /** + * Adds a new format to the available formats. + *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param excelFormatStr + * The data format string + * @param format + * A Format instance + */ + public void addFormat(String excelFormatStr, Format format) { + formats.put(excelFormatStr, format); + } + + // Some custom formats + + /** + * @return a DecimalFormat with parseIntegerOnly set true + */ + private static DecimalFormat createIntegerOnlyFormat(String fmt) { + DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT); + DecimalFormat result = new DecimalFormat(fmt, dsf); + result.setParseIntegerOnly(true); + return result; + } + + /** + * Enables excel style rounding mode (round half up) on the Decimal Format given. + */ + public static void setExcelStyleRoundingMode(DecimalFormat format) { + setExcelStyleRoundingMode(format, RoundingMode.HALF_UP); + } + + /** + * Enables custom rounding mode on the given Decimal Format. + * + * @param format + * DecimalFormat + * @param roundingMode + * RoundingMode + */ + public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { + format.setRoundingMode(roundingMode); + } + + /** + * Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class SSNFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private SSNFormat() { + // enforce singleton + } + + /** 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); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class ZipPlusFourFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private ZipPlusFourFormat() { + // enforce singleton + } + + /** 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); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class PhoneFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + + private PhoneFormat() { + // enforce singleton + } + + /** Format a number as a phone number */ + public static String format(Number num) { + String result = df.format(num); + StringBuilder sb = new StringBuilder(); + String seg1, seg2, seg3; + int len = result.length(); + if (len <= 4) { + return result; + } + + seg3 = result.substring(len - 4, len); + seg2 = result.substring(Math.max(0, len - 7), len - 4); + seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7)); + + if (seg1.trim().length() > 0) { + sb.append('(').append(seg1).append(") "); + } + if (seg2.trim().length() > 0) { + sb.append(seg2).append('-'); + } + sb.append(seg3); + return sb.toString(); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + +} diff --git a/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java b/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java index 921d70a..12cc8d3 100644 --- a/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java +++ b/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java @@ -1,5 +1,7 @@ package com.alibaba.excel.metadata; +import java.util.Locale; + /** * Global configuration * @@ -18,6 +20,11 @@ public class GlobalConfiguration { * @return */ private Boolean use1904windowing; + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + */ + private Locale locale; public Boolean getUse1904windowing() { return use1904windowing; @@ -34,4 +41,12 @@ public class GlobalConfiguration { public void setAutoTrim(Boolean autoTrim) { this.autoTrim = autoTrim; } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/Head.java b/src/main/java/com/alibaba/excel/metadata/Head.java index c1cea10..c578cbb 100644 --- a/src/main/java/com/alibaba/excel/metadata/Head.java +++ b/src/main/java/com/alibaba/excel/metadata/Head.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.excel.metadata.property.ColumnWidthProperty; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.StyleProperty; /** * excel head @@ -35,6 +38,26 @@ public class Head { * column with */ private ColumnWidthProperty columnWidthProperty; + /** + * Loop merge + */ + private LoopMergeProperty loopMergeProperty; + /** + * Head style + */ + private StyleProperty headStyleProperty; + /** + * Content style + */ + private StyleProperty contentStyleProperty; + /** + * Head font + */ + private FontProperty headFontProperty; + /** + * Content font + */ + private FontProperty contentFontProperty; public Head(Integer columnIndex, String fieldName, List headNameList, Boolean forceIndex, Boolean forceName) { @@ -95,4 +118,44 @@ public class Head { public void setForceName(Boolean forceName) { this.forceName = forceName; } + + public LoopMergeProperty getLoopMergeProperty() { + return loopMergeProperty; + } + + public void setLoopMergeProperty(LoopMergeProperty loopMergeProperty) { + this.loopMergeProperty = loopMergeProperty; + } + + public StyleProperty getHeadStyleProperty() { + return headStyleProperty; + } + + public void setHeadStyleProperty(StyleProperty headStyleProperty) { + this.headStyleProperty = headStyleProperty; + } + + public StyleProperty getContentStyleProperty() { + return contentStyleProperty; + } + + public void setContentStyleProperty(StyleProperty contentStyleProperty) { + this.contentStyleProperty = contentStyleProperty; + } + + public FontProperty getHeadFontProperty() { + return headFontProperty; + } + + public void setHeadFontProperty(FontProperty headFontProperty) { + this.headFontProperty = headFontProperty; + } + + public FontProperty getContentFontProperty() { + return contentFontProperty; + } + + public void setContentFontProperty(FontProperty contentFontProperty) { + this.contentFontProperty = contentFontProperty; + } } 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 782f635..34109ad 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -1,7 +1,6 @@ package com.alibaba.excel.metadata.property; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -12,8 +11,6 @@ import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.annotation.ExcelIgnore; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; @@ -21,7 +18,6 @@ import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.exception.ExcelCommonException; -import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.util.ClassUtils; @@ -86,10 +82,10 @@ public class ExcelHeadProperty { headIndex++; } headKind = HeadKindEnum.STRING; - } else { - // convert headClazz to head - initColumnProperties(holder, convertAllFiled); } + // convert headClazz to head + initColumnProperties(holder, convertAllFiled); + initHeadRowNumber(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind); @@ -163,10 +159,14 @@ public class ExcelHeadProperty { List tmpHeadList = new ArrayList(); boolean notForceName = excelProperty == null || excelProperty.value().length <= 0 || (excelProperty.value().length == 1 && StringUtils.isEmpty((excelProperty.value())[0])); - if (notForceName) { - tmpHeadList.add(field.getName()); + if (headMap.containsKey(index)) { + tmpHeadList.addAll(headMap.get(index).getHeadNameList()); } else { - Collections.addAll(tmpHeadList, excelProperty.value()); + if (notForceName) { + tmpHeadList.add(field.getName()); + } else { + Collections.addAll(tmpHeadList, excelProperty.value()); + } } Head head = new Head(index, field.getName(), tmpHeadList, forceIndex, !notForceName); ExcelContentProperty excelContentProperty = new ExcelContentProperty(); diff --git a/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java b/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java new file mode 100644 index 0000000..04caf9b --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java @@ -0,0 +1,180 @@ +package com.alibaba.excel.metadata.property; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class FontProperty { + /** + * The name for the font (i.e. Arial) + */ + private String fontName; + /** + * Height in the familiar unit of measure - points + */ + private Short fontHeightInPoints; + /** + * Whether to use italics or not + */ + private Boolean italic; + /** + * Whether to use a strikeout horizontal line through the text or not + */ + private Boolean strikeout; + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + private Short color; + /** + * Set normal,super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + private Short typeOffset; + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + private Byte underline; + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + private Integer charset; + /** + * Bold + */ + private Boolean bold; + + public static FontProperty build(HeadFontStyle headFontStyle) { + if (headFontStyle == null) { + return null; + } + FontProperty styleProperty = new FontProperty(); + styleProperty.setFontName(headFontStyle.fontName()); + styleProperty.setFontHeightInPoints(headFontStyle.fontHeightInPoints()); + styleProperty.setItalic(headFontStyle.italic()); + styleProperty.setStrikeout(headFontStyle.strikeout()); + styleProperty.setColor(headFontStyle.color()); + styleProperty.setTypeOffset(headFontStyle.typeOffset()); + styleProperty.setUnderline(headFontStyle.underline()); + styleProperty.setCharset(headFontStyle.charset()); + styleProperty.setBold(headFontStyle.bold()); + return styleProperty; + } + + public static FontProperty build(ContentFontStyle contentFontStyle) { + if (contentFontStyle == null) { + return null; + } + FontProperty styleProperty = new FontProperty(); + styleProperty.setFontName(contentFontStyle.fontName()); + styleProperty.setFontHeightInPoints(contentFontStyle.fontHeightInPoints()); + styleProperty.setItalic(contentFontStyle.italic()); + styleProperty.setStrikeout(contentFontStyle.strikeout()); + styleProperty.setColor(contentFontStyle.color()); + styleProperty.setTypeOffset(contentFontStyle.typeOffset()); + styleProperty.setUnderline(contentFontStyle.underline()); + styleProperty.setCharset(contentFontStyle.charset()); + styleProperty.setBold(contentFontStyle.bold()); + return styleProperty; + } + + public String getFontName() { + return fontName; + } + + public void setFontName(String fontName) { + this.fontName = fontName; + } + + public Short getFontHeightInPoints() { + return fontHeightInPoints; + } + + public void setFontHeightInPoints(Short fontHeightInPoints) { + this.fontHeightInPoints = fontHeightInPoints; + } + + public Boolean getItalic() { + return italic; + } + + public void setItalic(Boolean italic) { + this.italic = italic; + } + + public Boolean getStrikeout() { + return strikeout; + } + + public void setStrikeout(Boolean strikeout) { + this.strikeout = strikeout; + } + + public Short getColor() { + return color; + } + + public void setColor(Short color) { + this.color = color; + } + + public Short getTypeOffset() { + return typeOffset; + } + + public void setTypeOffset(Short typeOffset) { + this.typeOffset = typeOffset; + } + + public Byte getUnderline() { + return underline; + } + + public void setUnderline(Byte underline) { + this.underline = underline; + } + + public Integer getCharset() { + return charset; + } + + public void setCharset(Integer charset) { + this.charset = charset; + } + + public Boolean getBold() { + return bold; + } + + public void setBold(Boolean bold) { + this.bold = bold; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java b/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java new file mode 100644 index 0000000..05084bf --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class LoopMergeProperty { + /** + * Each row + */ + private int eachRow; + /** + * Extend column + */ + private int columnExtend; + + public LoopMergeProperty(int eachRow, int columnExtend) { + this.eachRow = eachRow; + this.columnExtend = columnExtend; + } + + public static LoopMergeProperty build(ContentLoopMerge contentLoopMerge) { + if (contentLoopMerge == null) { + return null; + } + return new LoopMergeProperty(contentLoopMerge.eachRow(), contentLoopMerge.columnExtend()); + } + + public int getEachRow() { + return eachRow; + } + + public void setEachRow(int eachRow) { + this.eachRow = eachRow; + } + + public int getColumnExtend() { + return columnExtend; + } + + public void setColumnExtend(int columnExtend) { + this.columnExtend = columnExtend; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java b/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java new file mode 100644 index 0000000..c509219 --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java @@ -0,0 +1,74 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.OnceAbsoluteMerge; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class OnceAbsoluteMergeProperty { + /** + * First row + */ + private int firstRowIndex; + /** + * Last row + */ + private int lastRowIndex; + /** + * First column + */ + private int firstColumnIndex; + /** + * Last row + */ + private int lastColumnIndex; + + public OnceAbsoluteMergeProperty(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) { + this.firstRowIndex = firstRowIndex; + this.lastRowIndex = lastRowIndex; + this.firstColumnIndex = firstColumnIndex; + this.lastColumnIndex = lastColumnIndex; + } + + public static OnceAbsoluteMergeProperty build(OnceAbsoluteMerge onceAbsoluteMerge) { + if (onceAbsoluteMerge == null) { + return null; + } + return new OnceAbsoluteMergeProperty(onceAbsoluteMerge.firstRowIndex(), onceAbsoluteMerge.lastRowIndex(), + onceAbsoluteMerge.firstColumnIndex(), onceAbsoluteMerge.lastColumnIndex()); + } + + public int getFirstRowIndex() { + return firstRowIndex; + } + + public void setFirstRowIndex(int firstRowIndex) { + this.firstRowIndex = firstRowIndex; + } + + public int getLastRowIndex() { + return lastRowIndex; + } + + public void setLastRowIndex(int lastRowIndex) { + this.lastRowIndex = lastRowIndex; + } + + public int getFirstColumnIndex() { + return firstColumnIndex; + } + + public void setFirstColumnIndex(int firstColumnIndex) { + this.firstColumnIndex = firstColumnIndex; + } + + public int getLastColumnIndex() { + return lastColumnIndex; + } + + public void setLastColumnIndex(int lastColumnIndex) { + this.lastColumnIndex = lastColumnIndex; + } +} diff --git a/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java b/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java new file mode 100644 index 0000000..9222b10 --- /dev/null +++ b/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java @@ -0,0 +1,377 @@ +package com.alibaba.excel.metadata.property; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class StyleProperty { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + private Short dataFormat; + /** + * Set the font for this style + */ + private WriteFont writeFont; + /** + * Set the cell's using this style to be hidden + */ + private Boolean hidden; + + /** + * Set the cell's using this style to be locked + */ + private Boolean locked; + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + private Boolean quotePrefix; + /** + * Set the type of horizontal alignment for the cell + */ + private HorizontalAlignment horizontalAlignment; + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + * + */ + private Boolean wrapped; + /** + * Set the type of vertical alignment for the cell + */ + private VerticalAlignment verticalAlignment; + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + private Short rotation; + /** + * Set the number of spaces to indent the text in the cell + */ + private Short indent; + /** + * Set the type of border to use for the left border of the cell + */ + private BorderStyle borderLeft; + /** + * Set the type of border to use for the right border of the cell + */ + private BorderStyle borderRight; + /** + * Set the type of border to use for the top border of the cell + */ + private BorderStyle borderTop; + + /** + * Set the type of border to use for the bottom border of the cell + */ + private BorderStyle borderBottom; + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + private Short leftBorderColor; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + * + */ + private Short rightBorderColor; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + * + */ + private Short topBorderColor; + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + * + */ + private Short bottomBorderColor; + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + private FillPatternType fillPatternType; + + /** + * Set the background fill color. + * + * @see IndexedColors + * + */ + private Short fillBackgroundColor; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + * + */ + private Short fillForegroundColor; + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + private Boolean shrinkToFit; + + public static StyleProperty build(HeadStyle headStyle) { + if (headStyle == null) { + return null; + } + StyleProperty styleProperty = new StyleProperty(); + styleProperty.setDataFormat(headStyle.dataFormat()); + styleProperty.setHidden(headStyle.hidden()); + styleProperty.setLocked(headStyle.locked()); + styleProperty.setQuotePrefix(headStyle.quotePrefix()); + styleProperty.setHorizontalAlignment(headStyle.horizontalAlignment()); + styleProperty.setWrapped(headStyle.wrapped()); + styleProperty.setVerticalAlignment(headStyle.verticalAlignment()); + styleProperty.setRotation(headStyle.rotation()); + styleProperty.setIndent(headStyle.indent()); + styleProperty.setBorderLeft(headStyle.borderLeft()); + styleProperty.setBorderRight(headStyle.borderRight()); + styleProperty.setBorderTop(headStyle.borderTop()); + styleProperty.setBorderBottom(headStyle.borderBottom()); + styleProperty.setLeftBorderColor(headStyle.leftBorderColor()); + styleProperty.setRightBorderColor(headStyle.rightBorderColor()); + styleProperty.setTopBorderColor(headStyle.topBorderColor()); + styleProperty.setBottomBorderColor(headStyle.bottomBorderColor()); + styleProperty.setFillPatternType(headStyle.fillPatternType()); + styleProperty.setFillBackgroundColor(headStyle.fillBackgroundColor()); + styleProperty.setFillForegroundColor(headStyle.fillForegroundColor()); + styleProperty.setShrinkToFit(headStyle.shrinkToFit()); + return styleProperty; + } + + public static StyleProperty build(ContentStyle contentStyle) { + if (contentStyle == null) { + return null; + } + StyleProperty styleProperty = new StyleProperty(); + styleProperty.setDataFormat(contentStyle.dataFormat()); + styleProperty.setHidden(contentStyle.hidden()); + styleProperty.setLocked(contentStyle.locked()); + styleProperty.setQuotePrefix(contentStyle.quotePrefix()); + styleProperty.setHorizontalAlignment(contentStyle.horizontalAlignment()); + styleProperty.setWrapped(contentStyle.wrapped()); + styleProperty.setVerticalAlignment(contentStyle.verticalAlignment()); + styleProperty.setRotation(contentStyle.rotation()); + styleProperty.setIndent(contentStyle.indent()); + styleProperty.setBorderLeft(contentStyle.borderLeft()); + styleProperty.setBorderRight(contentStyle.borderRight()); + styleProperty.setBorderTop(contentStyle.borderTop()); + styleProperty.setBorderBottom(contentStyle.borderBottom()); + styleProperty.setLeftBorderColor(contentStyle.leftBorderColor()); + styleProperty.setRightBorderColor(contentStyle.rightBorderColor()); + styleProperty.setTopBorderColor(contentStyle.topBorderColor()); + styleProperty.setBottomBorderColor(contentStyle.bottomBorderColor()); + styleProperty.setFillPatternType(contentStyle.fillPatternType()); + styleProperty.setFillBackgroundColor(contentStyle.fillBackgroundColor()); + styleProperty.setFillForegroundColor(contentStyle.fillForegroundColor()); + styleProperty.setShrinkToFit(contentStyle.shrinkToFit()); + return styleProperty; + } + + public Short getDataFormat() { + return dataFormat; + } + + public void setDataFormat(Short dataFormat) { + this.dataFormat = dataFormat; + } + + public WriteFont getWriteFont() { + return writeFont; + } + + public void setWriteFont(WriteFont writeFont) { + this.writeFont = writeFont; + } + + public Boolean getHidden() { + return hidden; + } + + public void setHidden(Boolean hidden) { + this.hidden = hidden; + } + + public Boolean getLocked() { + return locked; + } + + public void setLocked(Boolean locked) { + this.locked = locked; + } + + public Boolean getQuotePrefix() { + return quotePrefix; + } + + public void setQuotePrefix(Boolean quotePrefix) { + this.quotePrefix = quotePrefix; + } + + public HorizontalAlignment getHorizontalAlignment() { + return horizontalAlignment; + } + + public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) { + this.horizontalAlignment = horizontalAlignment; + } + + public Boolean getWrapped() { + return wrapped; + } + + public void setWrapped(Boolean wrapped) { + this.wrapped = wrapped; + } + + public VerticalAlignment getVerticalAlignment() { + return verticalAlignment; + } + + public void setVerticalAlignment(VerticalAlignment verticalAlignment) { + this.verticalAlignment = verticalAlignment; + } + + public Short getRotation() { + return rotation; + } + + public void setRotation(Short rotation) { + this.rotation = rotation; + } + + public Short getIndent() { + return indent; + } + + public void setIndent(Short indent) { + this.indent = indent; + } + + public BorderStyle getBorderLeft() { + return borderLeft; + } + + public void setBorderLeft(BorderStyle borderLeft) { + this.borderLeft = borderLeft; + } + + public BorderStyle getBorderRight() { + return borderRight; + } + + public void setBorderRight(BorderStyle borderRight) { + this.borderRight = borderRight; + } + + public BorderStyle getBorderTop() { + return borderTop; + } + + public void setBorderTop(BorderStyle borderTop) { + this.borderTop = borderTop; + } + + public BorderStyle getBorderBottom() { + return borderBottom; + } + + public void setBorderBottom(BorderStyle borderBottom) { + this.borderBottom = borderBottom; + } + + public Short getLeftBorderColor() { + return leftBorderColor; + } + + public void setLeftBorderColor(Short leftBorderColor) { + this.leftBorderColor = leftBorderColor; + } + + public Short getRightBorderColor() { + return rightBorderColor; + } + + public void setRightBorderColor(Short rightBorderColor) { + this.rightBorderColor = rightBorderColor; + } + + public Short getTopBorderColor() { + return topBorderColor; + } + + public void setTopBorderColor(Short topBorderColor) { + this.topBorderColor = topBorderColor; + } + + public Short getBottomBorderColor() { + return bottomBorderColor; + } + + public void setBottomBorderColor(Short bottomBorderColor) { + this.bottomBorderColor = bottomBorderColor; + } + + public FillPatternType getFillPatternType() { + return fillPatternType; + } + + public void setFillPatternType(FillPatternType fillPatternType) { + this.fillPatternType = fillPatternType; + } + + public Short getFillBackgroundColor() { + return fillBackgroundColor; + } + + public void setFillBackgroundColor(Short fillBackgroundColor) { + this.fillBackgroundColor = fillBackgroundColor; + } + + public Short getFillForegroundColor() { + return fillForegroundColor; + } + + public void setFillForegroundColor(Short fillForegroundColor) { + this.fillForegroundColor = fillForegroundColor; + } + + public Boolean getShrinkToFit() { + return shrinkToFit; + } + + public void setShrinkToFit(Boolean shrinkToFit) { + this.shrinkToFit = shrinkToFit; + } +} diff --git a/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java b/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java new file mode 100644 index 0000000..4e5e370 --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.read.builder; + +import java.util.ArrayList; + +import com.alibaba.excel.metadata.AbstractParameterBuilder; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.ReadBasicParameter; + +/** + * Build ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelReaderParameterBuilder extends AbstractParameterBuilder { + /** + * Count the number of added heads when read sheet. + * + *

+ * 0 - This Sheet has no head ,since the first row are the data + *

+ * 1 - This Sheet has one row head , this is the default + *

+ * 2 - This Sheet has two row head ,since the third row is the data + * + * @param headRowNumber + * @return + */ + public T headRowNumber(Integer headRowNumber) { + parameter().setHeadRowNumber(headRowNumber); + return self(); + } + + /** + * Custom type listener run after default + * + * @param readListener + * @return + */ + public T registerReadListener(ReadListener readListener) { + if (parameter().getCustomReadListenerList() == null) { + parameter().setCustomReadListenerList(new ArrayList()); + } + parameter().getCustomReadListenerList().add(readListener); + return self(); + } +} diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index 0c63726..81f3586 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -2,7 +2,7 @@ package com.alibaba.excel.read.builder; import java.io.File; import java.io.InputStream; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import javax.xml.parsers.SAXParserFactory; @@ -11,10 +11,10 @@ import com.alibaba.excel.ExcelReader; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.event.SyncReadListener; import com.alibaba.excel.read.listener.ModelBuildEventListener; -import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.support.ExcelTypeEnum; @@ -23,7 +23,7 @@ import com.alibaba.excel.support.ExcelTypeEnum; * * @author Jiaju Zhuang */ -public class ExcelReaderBuilder { +public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder { /** * Workbook */ @@ -133,98 +133,6 @@ public class ExcelReaderBuilder { return this; } - /** - * Count the number of added heads when read sheet. - * - *

- * 0 - This Sheet has no head ,since the first row are the data - *

- * 1 - This Sheet has one row head , this is the default - *

- * 2 - This Sheet has two row head ,since the third row is the data - * - * @param headRowNumber - * @return - */ - public ExcelReaderBuilder headRowNumber(Integer headRowNumber) { - readWorkbook.setHeadRowNumber(headRowNumber); - return this; - } - - /** - * You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)} - * - * @param head - * @return - */ - public ExcelReaderBuilder head(List> head) { - readWorkbook.setHead(head); - return this; - } - - /** - * You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)} - * - * @param clazz - * @return - */ - public ExcelReaderBuilder head(Class clazz) { - readWorkbook.setClazz(clazz); - return this; - } - - /** - * Custom type conversions override the default. - * - * @param converter - * @return - */ - public ExcelReaderBuilder registerConverter(Converter converter) { - if (readWorkbook.getCustomConverterList() == null) { - readWorkbook.setCustomConverterList(new ArrayList()); - } - readWorkbook.getCustomConverterList().add(converter); - return this; - } - - /** - * Custom type listener run after default - * - * @param readListener - * @return - */ - public ExcelReaderBuilder registerReadListener(ReadListener readListener) { - if (readWorkbook.getCustomReadListenerList() == null) { - readWorkbook.setCustomReadListenerList(new ArrayList()); - } - readWorkbook.getCustomReadListenerList().add(readListener); - return this; - } - - /** - * true if date uses 1904 windowing, or false if using 1900 date windowing. - * - * default is false - * - * @param use1904windowing - * @return - */ - public ExcelReaderBuilder use1904windowing(Boolean use1904windowing) { - readWorkbook.setUse1904windowing(use1904windowing); - return this; - } - - /** - * Automatic trim includes sheet name and content - * - * @param autoTrim - * @return - */ - public ExcelReaderBuilder autoTrim(Boolean autoTrim) { - readWorkbook.setAutoTrim(autoTrim); - return this; - } - /** * Whether the encryption * @@ -253,6 +161,21 @@ public class ExcelReaderBuilder { return this; } + /** + * Read some extra information, not by default + * + * @param extraType + * extra information type + * @return + */ + public ExcelReaderBuilder extraRead(CellExtraTypeEnum extraType) { + if (readWorkbook.getExtraReadSet() == null) { + readWorkbook.setExtraReadSet(new HashSet()); + } + readWorkbook.getExtraReadSet().add(extraType); + return this; + } + /** * Whether to use the default listener, which is used by default. *

@@ -270,11 +193,24 @@ public class ExcelReaderBuilder { return new ExcelReader(readWorkbook); } - public ExcelReader doReadAll() { + public void doReadAll() { + ExcelReader excelReader = build(); + excelReader.readAll(); + excelReader.finish(); + } + + /** + * Synchronous reads return results + * + * @return + */ + public List doReadAllSync() { + SyncReadListener syncReadListener = new SyncReadListener(); + registerReadListener(syncReadListener); ExcelReader excelReader = build(); excelReader.readAll(); excelReader.finish(); - return excelReader; + return (List)syncReadListener.getList(); } public ExcelReaderSheetBuilder sheet() { @@ -299,4 +235,9 @@ public class ExcelReaderBuilder { } return excelReaderSheetBuilder; } + + @Override + protected ReadWorkbook parameter() { + return readWorkbook; + } } diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java index d494e23..84ec6ba 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java @@ -1,14 +1,11 @@ package com.alibaba.excel.read.builder; -import java.util.ArrayList; import java.util.List; import com.alibaba.excel.ExcelReader; -import com.alibaba.excel.converters.Converter; import com.alibaba.excel.event.SyncReadListener; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.metadata.ReadSheet; /** @@ -16,7 +13,7 @@ import com.alibaba.excel.read.metadata.ReadSheet; * * @author Jiaju Zhuang */ -public class ExcelReaderSheetBuilder { +public class ExcelReaderSheetBuilder extends AbstractExcelReaderParameterBuilder { private ExcelReader excelReader; /** * Sheet @@ -54,98 +51,6 @@ public class ExcelReaderSheetBuilder { return this; } - /** - * Count the number of added heads when read sheet. - * - *

- * 0 - This Sheet has no head ,since the first row are the data - *

- * 1 - This Sheet has one row head , this is the default - *

- * 2 - This Sheet has two row head ,since the third row is the data - * - * @param headRowNumber - * @return - */ - public ExcelReaderSheetBuilder headRowNumber(Integer headRowNumber) { - readSheet.setHeadRowNumber(headRowNumber); - return this; - } - - /** - * You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)} - * - * @param head - * @return - */ - public ExcelReaderSheetBuilder head(List> head) { - readSheet.setHead(head); - return this; - } - - /** - * You can only choose one of the {@link ExcelReaderBuilder#head(List)} and {@link ExcelReaderBuilder#head(Class)} - * - * @param clazz - * @return - */ - public ExcelReaderSheetBuilder head(Class clazz) { - readSheet.setClazz(clazz); - return this; - } - - /** - * Custom type conversions override the default. - * - * @param converter - * @return - */ - public ExcelReaderSheetBuilder registerConverter(Converter converter) { - if (readSheet.getCustomConverterList() == null) { - readSheet.setCustomConverterList(new ArrayList()); - } - readSheet.getCustomConverterList().add(converter); - return this; - } - - /** - * Custom type listener run after default - * - * @param readListener - * @return - */ - public ExcelReaderSheetBuilder registerReadListener(ReadListener readListener) { - if (readSheet.getCustomReadListenerList() == null) { - readSheet.setCustomReadListenerList(new ArrayList()); - } - readSheet.getCustomReadListenerList().add(readListener); - return this; - } - - /** - * true if date uses 1904 windowing, or false if using 1900 date windowing. - * - * default is false - * - * @param use1904windowing - * @return - */ - public ExcelReaderSheetBuilder use1904windowing(Boolean use1904windowing) { - readSheet.setUse1904windowing(use1904windowing); - return this; - } - - /** - * Automatic trim includes sheet name and content - * - * @param autoTrim - * @return - */ - public ExcelReaderSheetBuilder autoTrim(Boolean autoTrim) { - readSheet.setAutoTrim(autoTrim); - return this; - } - public ReadSheet build() { return readSheet; } @@ -177,4 +82,8 @@ public class ExcelReaderSheetBuilder { return (List)syncReadListener.getList(); } + @Override + protected ReadSheet parameter() { + return readSheet; + } } 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 f5594db..78b57e0 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -62,6 +62,11 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } + int headSize = currentReadHolder.excelReadHeadProperty().getHeadMap().size(); + while (index < headSize) { + map.put(index, null); + index++; + } return map; } else { // Compatible with the old code the old code returns a list @@ -82,6 +87,11 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } + int headSize = currentReadHolder.excelReadHeadProperty().getHeadMap().size(); + while (index < headSize) { + list.add(null); + index++; + } return list; } } diff --git a/src/main/java/com/alibaba/excel/read/listener/ReadListener.java b/src/main/java/com/alibaba/excel/read/listener/ReadListener.java index 142c531..f1d341f 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ReadListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ReadListener.java @@ -5,6 +5,7 @@ import java.util.Map; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.Listener; import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.CellExtra; /** * Interface to listen for read results @@ -40,6 +41,16 @@ public interface ReadListener extends Listener { */ void invoke(T data, AnalysisContext context); + /** + * The current method is called when extra information is returned + * + * @param extra + * extra information + * @param context + * analysis context + */ + void extra(CellExtra extra, AnalysisContext context); + /** * if have something to do after all analysis * diff --git a/src/main/java/com/alibaba/excel/read/listener/ReadListenerRegistryCenter.java b/src/main/java/com/alibaba/excel/read/listener/ReadListenerRegistryCenter.java deleted file mode 100644 index 07bc68c..0000000 --- a/src/main/java/com/alibaba/excel/read/listener/ReadListenerRegistryCenter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alibaba.excel.read.listener; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.read.listener.event.AnalysisFinishEvent; - -/** - * Registry center. - * - * @author jipengfei - */ -public interface ReadListenerRegistryCenter { - - /** - * register - * - * @param listener - * Analysis listener - */ - void register(AnalysisEventListener listener); - - /** - * Parse one row to notify all event listeners - * - * @param event - * parse event - * @param analysisContext - * Analysis context - */ - void notifyEndOneRow(AnalysisFinishEvent event, AnalysisContext analysisContext); - - /** - * Notify after all analysed - * - * @param analysisContext - * Analysis context - */ - void notifyAfterAllAnalysed(AnalysisContext analysisContext); -} 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 deleted file mode 100644 index 9749cf0..0000000 --- a/src/main/java/com/alibaba/excel/read/listener/event/AnalysisFinishEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.alibaba.excel.read.listener.event; - -import java.util.Map; - -import com.alibaba.excel.metadata.CellData; - -/** - * - * Event - * - * @author jipengfei - */ -public interface AnalysisFinishEvent { - /** - * Get result - * - * @return - */ - 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 deleted file mode 100644 index 879a59a..0000000 --- a/src/main/java/com/alibaba/excel/read/listener/event/EachRowAnalysisFinishEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.alibaba.excel.read.listener.event; - -import java.util.Map; - -import com.alibaba.excel.metadata.CellData; - -/** - * @author jipengfei - */ -public class EachRowAnalysisFinishEvent implements AnalysisFinishEvent { - private Map result; - - public EachRowAnalysisFinishEvent(Map content) { - this.result = content; - } - - @Override - public Map getAnalysisResult() { - return result; - } -} diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java index 7ddab56..c9e4db9 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -2,12 +2,14 @@ package com.alibaba.excel.read.metadata; import java.io.File; import java.io.InputStream; +import java.util.Set; import javax.xml.parsers.SAXParserFactory; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ModelBuildEventListener; import com.alibaba.excel.support.ExcelTypeEnum; @@ -83,6 +85,12 @@ public class ReadWorkbook extends ReadBasicParameter { * The {@link ModelBuildEventListener} is loaded by default to convert the object. */ private Boolean useDefaultListener; + /** + * Read some additional fields. None are read by default. + * + * @see CellExtraTypeEnum + */ + private Set extraReadSet; /** * The default is all excel objects.Default is true. *

@@ -212,4 +220,12 @@ public class ReadWorkbook extends ReadBasicParameter { public void setUseDefaultListener(Boolean useDefaultListener) { this.useDefaultListener = useDefaultListener; } + + public Set getExtraReadSet() { + return extraReadSet; + } + + public void setExtraReadSet(Set extraReadSet) { + this.extraReadSet = extraReadSet; + } } 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 7133f99..a0bf7f1 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 @@ -3,41 +3,27 @@ package com.alibaba.excel.read.metadata.holder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.converters.DefaultConverterLoader; -import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HolderEnum; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.exception.ExcelAnalysisStopException; import com.alibaba.excel.metadata.AbstractHolder; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.read.listener.ModelBuildEventListener; import com.alibaba.excel.read.listener.ReadListener; -import com.alibaba.excel.read.listener.ReadListenerRegistryCenter; -import com.alibaba.excel.read.listener.event.AnalysisFinishEvent; import com.alibaba.excel.read.metadata.ReadBasicParameter; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; -import com.alibaba.excel.util.CollectionUtils; -import com.alibaba.excel.util.ConverterUtils; -import com.alibaba.excel.util.StringUtils; /** * Read Holder * * @author Jiaju Zhuang */ -public abstract class AbstractReadHolder extends AbstractHolder implements ReadHolder, ReadListenerRegistryCenter { +public abstract class AbstractReadHolder extends AbstractHolder implements ReadHolder { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractReadHolder.class); /** @@ -117,123 +103,6 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH } } - @Override - public void register(AnalysisEventListener listener) { - readListenerList.add(listener); - } - - @Override - public void notifyEndOneRow(AnalysisFinishEvent event, AnalysisContext analysisContext) { - Map cellDataMap = event.getAnalysisResult(); - if (CollectionUtils.isEmpty(cellDataMap)) { - if (LOGGER.isDebugEnabled()) { - LOGGER.warn("Empty row!"); - } - if (analysisContext.readWorkbookHolder().getIgnoreEmptyRow()) { - return; - } - } - ReadRowHolder readRowHolder = analysisContext.readRowHolder(); - readRowHolder.setCurrentRowAnalysisResult(cellDataMap); - int rowIndex = readRowHolder.getRowIndex(); - int currentheadRowNumber = analysisContext.readSheetHolder().getHeadRowNumber(); - - if (rowIndex >= currentheadRowNumber) { - // Now is data - for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { - try { - readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext); - } catch (Exception e) { - for (ReadListener readListenerException : analysisContext.currentReadHolder().readListenerList()) { - try { - readListenerException.onException(e, analysisContext); - } catch (Exception exception) { - throw new ExcelAnalysisException(exception.getMessage(), exception); - } - } - break; - } - if (!readListener.hasNext(analysisContext)) { - throw new ExcelAnalysisStopException(); - } - } - } else { - // Last head column - if (currentheadRowNumber == rowIndex + 1) { - buildHead(analysisContext, cellDataMap); - } - // Now is header - for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { - try { - readListener.invokeHead(cellDataMap, analysisContext); - } catch (Exception e) { - for (ReadListener readListenerException : analysisContext.currentReadHolder().readListenerList()) { - try { - readListenerException.onException(e, analysisContext); - } catch (Exception exception) { - throw new ExcelAnalysisException(exception.getMessage(), exception); - } - } - break; - } - if (!readListener.hasNext(analysisContext)) { - throw new ExcelAnalysisStopException(); - } - } - } - } - - @Override - public void notifyAfterAllAnalysed(AnalysisContext analysisContext) { - for (ReadListener readListener : readListenerList) { - readListener.doAfterAllAnalysed(analysisContext); - } - } - - private void buildHead(AnalysisContext analysisContext, Map cellDataMap) { - if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { - return; - } - Map dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext); - ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); - Map headMapData = excelHeadPropertyData.getHeadMap(); - Map contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); - Map tmpHeadMap = new HashMap(headMapData.size() * 4 / 3 + 1); - Map tmpContentPropertyMap = - new HashMap(contentPropertyMapData.size() * 4 / 3 + 1); - for (Map.Entry entry : headMapData.entrySet()) { - Head headData = entry.getValue(); - if (headData.getForceIndex() || !headData.getForceName()) { - tmpHeadMap.put(entry.getKey(), headData); - tmpContentPropertyMap.put(entry.getKey(), contentPropertyMapData.get(entry.getKey())); - continue; - } - List headNameList = headData.getHeadNameList(); - String headName = headNameList.get(headNameList.size() - 1); - for (Map.Entry stringEntry : dataMap.entrySet()) { - if (stringEntry == null) { - continue; - } - String headString = stringEntry.getValue(); - Integer stringKey = stringEntry.getKey(); - if (StringUtils.isEmpty(headString)) { - continue; - } - if (analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) { - headString = headString.trim(); - } - if (headName.equals(headString)) { - headData.setColumnIndex(stringKey); - tmpHeadMap.put(stringKey, headData); - tmpContentPropertyMap.put(stringKey, contentPropertyMapData.get(entry.getKey())); - break; - } - } - } - excelHeadPropertyData.setHeadMap(tmpHeadMap); - excelHeadPropertyData.setContentPropertyMap(tmpContentPropertyMap); - } - public List getReadListenerList() { return readListenerList; } diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java index 9916015..d81df87 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java @@ -1,6 +1,10 @@ package com.alibaba.excel.read.metadata.holder; +import java.util.Map; + import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; import com.alibaba.excel.metadata.GlobalConfiguration; import com.alibaba.excel.metadata.Holder; @@ -14,7 +18,14 @@ public class ReadRowHolder implements Holder { * Returns row index of a row in the sheet that contains this cell.Start form 0. */ private Integer rowIndex; - + /** + * Row type + */ + private RowTypeEnum rowType; + /** + * Cell map + */ + private Map cellMap; /** * The result of the previous listener */ @@ -24,9 +35,12 @@ public class ReadRowHolder implements Holder { */ private GlobalConfiguration globalConfiguration; - public ReadRowHolder(Integer rowIndex, GlobalConfiguration globalConfiguration) { + public ReadRowHolder(Integer rowIndex, RowTypeEnum rowType, GlobalConfiguration globalConfiguration, + Map cellMap) { this.rowIndex = rowIndex; + this.rowType = rowType; this.globalConfiguration = globalConfiguration; + this.cellMap = cellMap; } public GlobalConfiguration getGlobalConfiguration() { @@ -53,6 +67,22 @@ public class ReadRowHolder implements Holder { this.rowIndex = rowIndex; } + public RowTypeEnum getRowType() { + return rowType; + } + + public void setRowType(RowTypeEnum rowType) { + this.rowType = rowType; + } + + public Map getCellMap() { + return cellMap; + } + + public void setCellMap(Map cellMap) { + this.cellMap = cellMap; + } + @Override public HolderEnum holderType() { return HolderEnum.ROW; diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java index 84dd4c4..87185dc 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java @@ -1,6 +1,12 @@ package com.alibaba.excel.read.metadata.holder; +import java.util.LinkedHashMap; +import java.util.Map; + import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.CellExtra; import com.alibaba.excel.read.metadata.ReadSheet; /** @@ -29,6 +35,22 @@ public class ReadSheetHolder extends AbstractReadHolder { * Gets the total number of rows , data may be inaccurate */ private Integer approximateTotalRowNumber; + /** + * Data storage of the current row. + */ + private Map cellMap; + /** + * Data storage of the current extra cell. + */ + private CellExtra cellExtra; + /** + * Index of the current row. + */ + private Integer rowIndex; + /** + * Current CellData + */ + private CellData tempCellData; public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled()); @@ -36,6 +58,7 @@ public class ReadSheetHolder extends AbstractReadHolder { this.parentReadWorkbookHolder = readWorkbookHolder; this.sheetNo = readSheet.getSheetNo(); this.sheetName = readSheet.getSheetName(); + this.cellMap = new LinkedHashMap(); } public ReadSheet getReadSheet() { @@ -71,7 +94,6 @@ public class ReadSheetHolder extends AbstractReadHolder { } /** - * * Approximate total number of rows * * @return @@ -95,6 +117,38 @@ public class ReadSheetHolder extends AbstractReadHolder { this.approximateTotalRowNumber = approximateTotalRowNumber; } + public Map getCellMap() { + return cellMap; + } + + public void setCellMap(Map cellMap) { + this.cellMap = cellMap; + } + + public Integer getRowIndex() { + return rowIndex; + } + + public void setRowIndex(Integer rowIndex) { + this.rowIndex = rowIndex; + } + + public CellData getTempCellData() { + return tempCellData; + } + + public void setTempCellData(CellData tempCellData) { + this.tempCellData = tempCellData; + } + + public CellExtra getCellExtra() { + return cellExtra; + } + + public void setCellExtra(CellExtra cellExtra) { + this.cellExtra = cellExtra; + } + @Override public HolderEnum holderType() { return HolderEnum.SHEET; diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index cbda93f..375e32c 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -1,24 +1,21 @@ package com.alibaba.excel.read.metadata.holder; -import java.io.BufferedInputStream; import java.io.File; import java.io.InputStream; import java.util.HashSet; +import java.util.List; import java.util.Set; -import javax.xml.parsers.SAXParserFactory; - -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; - import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.EternalReadCacheSelector; import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.cache.selector.SimpleReadCacheSelector; import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.support.ExcelTypeEnum; @@ -86,16 +83,24 @@ public class ReadWorkbookHolder extends AbstractReadHolder { */ private String password; /** - * SAXParserFactory used when reading xlsx. - *

- * The default will automatically find. - *

- * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * Read some additional fields. None are read by default. * - * @see SAXParserFactory#newInstance() - * @see SAXParserFactory#newInstance(String, ClassLoader) + * @see CellExtraTypeEnum */ - private String xlsxSAXParserFactoryName; + private Set extraReadSet; + /** + * Actual sheet data + */ + private List actualSheetDataList; + /** + * Parameter sheet data + */ + private List parameterSheetDataList; + /** + * Read all + */ + private Boolean readAll; + /** * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. @@ -115,35 +120,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder { * Prevent repeating sheet */ private Set hasReadSheet; - /** - * Package - */ - private OPCPackage opcPackage; - /** - * File System - */ - private POIFSFileSystem poifsFileSystem; - - /** - * Excel 2003 cannot read specific sheet. It can only read sheet by sheet.So when you specify one sheet, you ignore - * the others. - */ - private Boolean ignoreRecord03; public ReadWorkbookHolder(ReadWorkbook readWorkbook) { super(readWorkbook, null, readWorkbook.getConvertAllFiled()); this.readWorkbook = readWorkbook; if (readWorkbook.getInputStream() != null) { - if (readWorkbook.getInputStream().markSupported()) { - this.inputStream = readWorkbook.getInputStream(); - } else { - this.inputStream = new BufferedInputStream(readWorkbook.getInputStream()); - } + this.inputStream = readWorkbook.getInputStream(); } this.file = readWorkbook.getFile(); - if (file == null && inputStream == null) { - throw new ExcelAnalysisException("File and inputStream must be a non-null."); - } if (readWorkbook.getMandatoryUseInputStream() == null) { this.mandatoryUseInputStream = Boolean.FALSE; } else { @@ -155,13 +139,6 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.autoCloseStream = readWorkbook.getAutoCloseStream(); } - // The type of excel is read according to the judgment.Because encrypted XLSX needs to be specified as XLS to - // properly parse. - this.excelType = ExcelTypeEnum.valueOf(file, inputStream, readWorkbook.getExcelType()); - - if (ExcelTypeEnum.XLS == excelType && getGlobalConfiguration().getUse1904windowing() == null) { - getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); - } this.customObject = readWorkbook.getCustomObject(); if (readWorkbook.getIgnoreEmptyRow() == null) { this.ignoreEmptyRow = Boolean.TRUE; @@ -185,9 +162,12 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } else { this.defaultReturnMap = readWorkbook.getDefaultReturnMap(); } - this.xlsxSAXParserFactoryName = readWorkbook.getXlsxSAXParserFactoryName(); + if (readWorkbook.getExtraReadSet() == null) { + this.extraReadSet = new HashSet(); + } else { + this.extraReadSet = readWorkbook.getExtraReadSet(); + } this.hasReadSheet = new HashSet(); - this.ignoreRecord03 = Boolean.FALSE; this.password = readWorkbook.getPassword(); } @@ -303,44 +283,44 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.defaultReturnMap = defaultReturnMap; } - public OPCPackage getOpcPackage() { - return opcPackage; + public String getPassword() { + return password; } - public void setOpcPackage(OPCPackage opcPackage) { - this.opcPackage = opcPackage; + public void setPassword(String password) { + this.password = password; } - public POIFSFileSystem getPoifsFileSystem() { - return poifsFileSystem; + public Set getExtraReadSet() { + return extraReadSet; } - public void setPoifsFileSystem(POIFSFileSystem poifsFileSystem) { - this.poifsFileSystem = poifsFileSystem; + public void setExtraReadSet(Set extraReadSet) { + this.extraReadSet = extraReadSet; } - public Boolean getIgnoreRecord03() { - return ignoreRecord03; + public List getActualSheetDataList() { + return actualSheetDataList; } - public void setIgnoreRecord03(Boolean ignoreRecord03) { - this.ignoreRecord03 = ignoreRecord03; + public void setActualSheetDataList(List actualSheetDataList) { + this.actualSheetDataList = actualSheetDataList; } - public String getPassword() { - return password; + public List getParameterSheetDataList() { + return parameterSheetDataList; } - public void setPassword(String password) { - this.password = password; + public void setParameterSheetDataList(List parameterSheetDataList) { + this.parameterSheetDataList = parameterSheetDataList; } - public String getXlsxSAXParserFactoryName() { - return xlsxSAXParserFactoryName; + public Boolean getReadAll() { + return readAll; } - public void setXlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) { - this.xlsxSAXParserFactoryName = xlsxSAXParserFactoryName; + public void setReadAll(Boolean readAll) { + this.readAll = readAll; } @Override diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java new file mode 100644 index 0000000..d0171ff --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java @@ -0,0 +1,72 @@ +package com.alibaba.excel.read.metadata.holder.xls; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +public class XlsReadSheetHolder extends ReadSheetHolder { + /** + * Row type.Temporary storage, last set in ReadRowHolder. + */ + private RowTypeEnum tempRowType; + /** + * Ignore record. + */ + private Boolean ignoreRecord; + /** + * Temp object index. + */ + private Integer tempObjectIndex; + /** + * Temp object index. + */ + private Map objectCacheMap; + + public XlsReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + ignoreRecord = Boolean.FALSE; + tempRowType = RowTypeEnum.EMPTY; + objectCacheMap = new HashMap(16); + } + + public RowTypeEnum getTempRowType() { + return tempRowType; + } + + public void setTempRowType(RowTypeEnum tempRowType) { + this.tempRowType = tempRowType; + } + + public Boolean getIgnoreRecord() { + return ignoreRecord; + } + + public void setIgnoreRecord(Boolean ignoreRecord) { + this.ignoreRecord = ignoreRecord; + } + + public Integer getTempObjectIndex() { + return tempObjectIndex; + } + + public void setTempObjectIndex(Integer tempObjectIndex) { + this.tempObjectIndex = tempObjectIndex; + } + + public Map getObjectCacheMap() { + return objectCacheMap; + } + + public void setObjectCacheMap(Map objectCacheMap) { + this.objectCacheMap = objectCacheMap; + } +} diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java new file mode 100644 index 0000000..70c1c03 --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java @@ -0,0 +1,103 @@ +package com.alibaba.excel.read.metadata.holder.xls; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +public class XlsReadWorkbookHolder extends ReadWorkbookHolder { + /** + * File System + */ + private POIFSFileSystem poifsFileSystem; + /** + * Format tracking HSSFListener + */ + private FormatTrackingHSSFListener formatTrackingHSSFListener; + /** + * HSSFWorkbook + */ + private HSSFWorkbook hssfWorkbook; + /** + * Bound sheet record list. + */ + private List boundSheetRecordList; + /** + * Need read sheet. + */ + private Boolean needReadSheet; + /** + * Sheet Index + */ + private Integer readSheetIndex; + + public XlsReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook); + this.boundSheetRecordList = new ArrayList(); + this.needReadSheet = Boolean.TRUE; + setExcelType(ExcelTypeEnum.XLS); + if (getGlobalConfiguration().getUse1904windowing() == null) { + getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); + } + } + + public POIFSFileSystem getPoifsFileSystem() { + return poifsFileSystem; + } + + public void setPoifsFileSystem(POIFSFileSystem poifsFileSystem) { + this.poifsFileSystem = poifsFileSystem; + } + + public FormatTrackingHSSFListener getFormatTrackingHSSFListener() { + return formatTrackingHSSFListener; + } + + public void setFormatTrackingHSSFListener(FormatTrackingHSSFListener formatTrackingHSSFListener) { + this.formatTrackingHSSFListener = formatTrackingHSSFListener; + } + + public HSSFWorkbook getHssfWorkbook() { + return hssfWorkbook; + } + + public void setHssfWorkbook(HSSFWorkbook hssfWorkbook) { + this.hssfWorkbook = hssfWorkbook; + } + + public List getBoundSheetRecordList() { + return boundSheetRecordList; + } + + public void setBoundSheetRecordList(List boundSheetRecordList) { + this.boundSheetRecordList = boundSheetRecordList; + } + + public Boolean getNeedReadSheet() { + return needReadSheet; + } + + public void setNeedReadSheet(Boolean needReadSheet) { + this.needReadSheet = needReadSheet; + } + + public Integer getReadSheetIndex() { + return readSheetIndex; + } + + public void setReadSheetIndex(Integer readSheetIndex) { + this.readSheetIndex = readSheetIndex; + } +} diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java new file mode 100644 index 0000000..46b9281 --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java @@ -0,0 +1,69 @@ +package com.alibaba.excel.read.metadata.holder.xlsx; + +import java.util.Deque; +import java.util.LinkedList; + +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +public class XlsxReadSheetHolder extends ReadSheetHolder { + /** + * Record the label of the current operation to prevent NPE. + */ + private Deque tagDeque; + /** + * Current Column + */ + private Integer columnIndex; + /** + * Data for current label. + */ + private StringBuilder tempData; + /** + * Formula for current label. + */ + private StringBuilder tempFormula; + + public XlsxReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + this.tagDeque = new LinkedList(); + } + + public Deque getTagDeque() { + return tagDeque; + } + + public void setTagDeque(Deque tagDeque) { + this.tagDeque = tagDeque; + } + + public Integer getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(Integer columnIndex) { + this.columnIndex = columnIndex; + } + + public StringBuilder getTempData() { + return tempData; + } + + public void setTempData(StringBuilder tempData) { + this.tempData = tempData; + } + + public StringBuilder getTempFormula() { + return tempFormula; + } + + public void setTempFormula(StringBuilder tempFormula) { + this.tempFormula = tempFormula; + } +} diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java new file mode 100644 index 0000000..066131d --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.read.metadata.holder.xlsx; + +import javax.xml.parsers.SAXParserFactory; + +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.xssf.model.StylesTable; + +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +public class XlsxReadWorkbookHolder extends ReadWorkbookHolder { + /** + * Package + */ + private OPCPackage opcPackage; + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + private String saxParserFactoryName; + /** + * Current style information + */ + private StylesTable stylesTable; + + public XlsxReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook); + this.saxParserFactoryName = readWorkbook.getXlsxSAXParserFactoryName(); + setExcelType(ExcelTypeEnum.XLSX); + } + + public OPCPackage getOpcPackage() { + return opcPackage; + } + + public void setOpcPackage(OPCPackage opcPackage) { + this.opcPackage = opcPackage; + } + + public String getSaxParserFactoryName() { + return saxParserFactoryName; + } + + public void setSaxParserFactoryName(String saxParserFactoryName) { + this.saxParserFactoryName = saxParserFactoryName; + } + + public StylesTable getStylesTable() { + return stylesTable; + } + + public void setStylesTable(StylesTable stylesTable) { + this.stylesTable = stylesTable; + } +} diff --git a/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java b/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java new file mode 100644 index 0000000..360ae97 --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.read.processor; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * + * Event processor + * + * @author jipengfei + */ +public interface AnalysisEventProcessor { + /** + * Read extra information + * + * @param analysisContext + */ + void extra(AnalysisContext analysisContext); + + /** + * End row + * + * @param analysisContext + */ + void endRow(AnalysisContext analysisContext); + + /** + * Notify after all analysed + * + * @param analysisContext + * Analysis context + */ + void endSheet(AnalysisContext analysisContext); +} diff --git a/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java b/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java new file mode 100644 index 0000000..ddd1950 --- /dev/null +++ b/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java @@ -0,0 +1,157 @@ +package com.alibaba.excel.read.processor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; +import com.alibaba.excel.util.ConverterUtils; +import com.alibaba.excel.util.StringUtils; + +/** + * Analysis event + * + * @author jipengfei + */ +public class DefaultAnalysisEventProcessor implements AnalysisEventProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAnalysisEventProcessor.class); + + @Override + public void extra(AnalysisContext analysisContext) { + dealExtra(analysisContext); + } + + @Override + public void endRow(AnalysisContext analysisContext) { + if (RowTypeEnum.EMPTY.equals(analysisContext.readRowHolder().getRowType())) { + if (LOGGER.isDebugEnabled()) { + LOGGER.warn("Empty row!"); + } + if (analysisContext.readWorkbookHolder().getIgnoreEmptyRow()) { + return; + } + } + dealData(analysisContext); + } + + @Override + public void endSheet(AnalysisContext analysisContext) { + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + readListener.doAfterAllAnalysed(analysisContext); + } + } + + private void dealExtra(AnalysisContext analysisContext) { + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + try { + readListener.extra(analysisContext.readSheetHolder().getCellExtra(), analysisContext); + } catch (Exception e) { + onException(analysisContext, e); + break; + } + if (!readListener.hasNext(analysisContext)) { + throw new ExcelAnalysisStopException(); + } + } + } + + private void onException(AnalysisContext analysisContext, Exception e) { + for (ReadListener readListenerException : analysisContext.currentReadHolder().readListenerList()) { + try { + readListenerException.onException(e, analysisContext); + } catch (RuntimeException re) { + throw re; + } catch (Exception e1) { + throw new ExcelAnalysisException(e1.getMessage(), e1); + } + } + } + + private void dealData(AnalysisContext analysisContext) { + ReadRowHolder readRowHolder = analysisContext.readRowHolder(); + Map cellDataMap = (Map)readRowHolder.getCellMap(); + readRowHolder.setCurrentRowAnalysisResult(cellDataMap); + int rowIndex = readRowHolder.getRowIndex(); + int currentHeadRowNumber = analysisContext.readSheetHolder().getHeadRowNumber(); + + boolean isData = rowIndex >= currentHeadRowNumber; + + // Last head column + if (!isData && currentHeadRowNumber == rowIndex + 1) { + buildHead(analysisContext, cellDataMap); + } + // Now is data + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + try { + if (isData) { + readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext); + } else { + readListener.invokeHead(cellDataMap, analysisContext); + } + } catch (Exception e) { + onException(analysisContext, e); + break; + } + if (!readListener.hasNext(analysisContext)) { + throw new ExcelAnalysisStopException(); + } + } + } + + private void buildHead(AnalysisContext analysisContext, Map cellDataMap) { + if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { + return; + } + Map dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext); + ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); + Map headMapData = excelHeadPropertyData.getHeadMap(); + Map contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); + Map tmpHeadMap = new HashMap(headMapData.size() * 4 / 3 + 1); + Map tmpContentPropertyMap = + new HashMap(contentPropertyMapData.size() * 4 / 3 + 1); + for (Map.Entry entry : headMapData.entrySet()) { + Head headData = entry.getValue(); + if (headData.getForceIndex() || !headData.getForceName()) { + tmpHeadMap.put(entry.getKey(), headData); + tmpContentPropertyMap.put(entry.getKey(), contentPropertyMapData.get(entry.getKey())); + continue; + } + List headNameList = headData.getHeadNameList(); + String headName = headNameList.get(headNameList.size() - 1); + for (Map.Entry stringEntry : dataMap.entrySet()) { + if (stringEntry == null) { + continue; + } + String headString = stringEntry.getValue(); + Integer stringKey = stringEntry.getKey(); + if (StringUtils.isEmpty(headString)) { + continue; + } + if (analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + headString = headString.trim(); + } + if (headName.equals(headString)) { + headData.setColumnIndex(stringKey); + tmpHeadMap.put(stringKey, headData); + tmpContentPropertyMap.put(stringKey, contentPropertyMapData.get(entry.getKey())); + break; + } + } + } + excelHeadPropertyData.setHeadMap(tmpHeadMap); + excelHeadPropertyData.setContentPropertyMap(tmpContentPropertyMap); + } +} diff --git a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java index b4e41df..cdca07f 100644 --- a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java +++ b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java @@ -3,12 +3,14 @@ package com.alibaba.excel.support; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; import org.apache.poi.poifs.filesystem.FileMagic; +import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.util.StringUtils; /** * @author jipengfei @@ -29,44 +31,68 @@ public enum ExcelTypeEnum { this.setValue(value); } - public static ExcelTypeEnum valueOf(File file, InputStream inputStream, ExcelTypeEnum excelType) { + public static ExcelTypeEnum valueOf(ReadWorkbook readWorkbook) { + ExcelTypeEnum excelType = readWorkbook.getExcelType(); + if (excelType != null) { + return excelType; + } + File file = readWorkbook.getFile(); + InputStream inputStream = readWorkbook.getInputStream(); + if (file == null && inputStream == null) { + throw new ExcelAnalysisException("File and inputStream must be a non-null."); + } try { - FileMagic fileMagic; if (file != null) { - BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); - try { - fileMagic = FileMagic.valueOf(bufferedInputStream); - } finally { - bufferedInputStream.close(); + if (!file.exists()) { + throw new ExcelAnalysisException("File " + file.getAbsolutePath() + " not exists."); } - if (!FileMagic.OLE2.equals(fileMagic) && !FileMagic.OOXML.equals(fileMagic)) { - String fileName = file.getName(); - if (fileName.endsWith(XLSX.getValue())) { - return XLSX; - } else if (fileName.endsWith(XLS.getValue())) { - return XLS; - } else { - throw new ExcelCommonException("Unknown excel type."); + // If there is a password, use the FileMagic first + if (!StringUtils.isEmpty(readWorkbook.getPassword())) { + BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); + try { + return recognitionExcelType(bufferedInputStream); + } finally { + bufferedInputStream.close(); + } + } + // Use the name to determine the type + String fileName = file.getName(); + if (fileName.endsWith(XLSX.getValue())) { + return XLSX; + } else if (fileName.endsWith(XLS.getValue())) { + return XLS; + } + if (StringUtils.isEmpty(readWorkbook.getPassword())) { + BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); + try { + return recognitionExcelType(bufferedInputStream); + } finally { + bufferedInputStream.close(); } } - } else { - fileMagic = FileMagic.valueOf(inputStream); - } - if (FileMagic.OLE2.equals(fileMagic)) { - return XLS; - } - if (FileMagic.OOXML.equals(fileMagic)) { - return XLSX; } - } catch (IOException e) { - if (excelType != null) { - return excelType; + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + readWorkbook.setInputStream(inputStream); } + return recognitionExcelType(inputStream); + } catch (ExcelCommonException e) { + throw e; + } catch (ExcelAnalysisException e) { + throw e; + } catch (Exception e) { throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself", e); } - if (excelType != null) { - return excelType; + } + + private static ExcelTypeEnum recognitionExcelType(InputStream inputStream) throws Exception { + FileMagic fileMagic = FileMagic.valueOf(inputStream); + if (FileMagic.OLE2.equals(fileMagic)) { + return XLS; + } + if (FileMagic.OOXML.equals(fileMagic)) { + return XLSX; } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself"); diff --git a/src/main/java/com/alibaba/excel/util/DateUtils.java b/src/main/java/com/alibaba/excel/util/DateUtils.java index a581a95..815257b 100644 --- a/src/main/java/com/alibaba/excel/util/DateUtils.java +++ b/src/main/java/com/alibaba/excel/util/DateUtils.java @@ -1,10 +1,12 @@ package com.alibaba.excel.util; +import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; - -import com.alibaba.excel.exception.ExcelDataConvertException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; /** * Date utils @@ -12,6 +14,33 @@ import com.alibaba.excel.exception.ExcelDataConvertException; * @author Jiaju Zhuang **/ public class DateUtils { + /** + * Is a cache of dates + */ + private static final ThreadLocal> DATE_THREAD_LOCAL = + new ThreadLocal>(); + /** + * Is a cache of dates + */ + private static final ThreadLocal> DATE_FORMAT_THREAD_LOCAL = + new ThreadLocal>(); + + /** + * The following patterns are used in {@link #isADateFormat(Integer, String)} + */ + private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]"); + private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]"); + private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]"); + // add "\u5e74 \u6708 \u65e5" for Chinese/Japanese date format:2017 \u5e74 2 \u6708 7 \u65e5 + private static final Pattern date_ptrn3b = + Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u5e74\u6708\u65e5,. :\"\\\\]+0*[ampAMP/]*$"); + // elapsed time patterns: [h],[m] and [s] + private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]"); + // for format which start with "[DBNum1]" or "[DBNum2]" or "[DBNum3]" could be a Chinese date + private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]"); + // for format which start with "年" or "月" or "日" or "时" or "分" or "秒" could be a Chinese date + private static final Pattern date_ptrn6 = Pattern.compile("(年|月|日|时|分|秒)+"); + public static final String DATE_FORMAT_10 = "yyyy-MM-dd"; public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss"; public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss"; @@ -33,7 +62,7 @@ public class DateUtils { if (StringUtils.isEmpty(dateFormat)) { dateFormat = switchDateFormat(dateString); } - return new SimpleDateFormat(dateFormat).parse(dateString); + return getCacheDateFormat(dateFormat).parse(dateString); } /** @@ -99,6 +128,182 @@ public class DateUtils { if (StringUtils.isEmpty(dateFormat)) { dateFormat = DATE_FORMAT_19; } - return new SimpleDateFormat(dateFormat).format(date); + return getCacheDateFormat(dateFormat).format(date); + } + + private static DateFormat getCacheDateFormat(String dateFormat) { + Map dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get(); + if (dateFormatMap == null) { + dateFormatMap = new HashMap(); + DATE_FORMAT_THREAD_LOCAL.set(dateFormatMap); + } else { + SimpleDateFormat dateFormatCached = dateFormatMap.get(dateFormat); + if (dateFormatCached != null) { + return dateFormatCached; + } + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); + dateFormatMap.put(dateFormat, simpleDateFormat); + return simpleDateFormat; + } + + /** + * Determine if it is a date format. + * + * @param formatIndex + * @param formatString + * @return + */ + public static boolean isADateFormat(Integer formatIndex, String formatString) { + if (formatIndex == null) { + return false; + } + Map isDateCache = DATE_THREAD_LOCAL.get(); + if (isDateCache == null) { + isDateCache = new HashMap(); + DATE_THREAD_LOCAL.set(isDateCache); + } else { + Boolean isDateCachedData = isDateCache.get(formatIndex); + if (isDateCachedData != null) { + return isDateCachedData; + } + } + boolean isDate = isADateFormatUncached(formatIndex, formatString); + isDateCache.put(formatIndex, isDate); + return isDate; + } + + /** + * Determine if it is a date format. + * + * @param formatIndex + * @param formatString + * @return + */ + public static boolean isADateFormatUncached(Integer formatIndex, String formatString) { + // First up, is this an internal date format? + if (isInternalDateFormat(formatIndex)) { + return true; + } + if (StringUtils.isEmpty(formatString)) { + return false; + } + String fs = formatString; + final int length = fs.length(); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + char c = fs.charAt(i); + if (i < length - 1) { + char nc = fs.charAt(i + 1); + if (c == '\\') { + switch (nc) { + case '-': + case ',': + case '.': + case ' ': + case '\\': + // skip current '\' and continue to the next char + continue; + } + } else if (c == ';' && nc == '@') { + i++; + // skip ";@" duplets + continue; + } + } + sb.append(c); + } + fs = sb.toString(); + + // short-circuit if it indicates elapsed time: [h], [m] or [s] + if (date_ptrn4.matcher(fs).matches()) { + return true; + } + // If it starts with [DBNum1] or [DBNum2] or [DBNum3] + // then it could be a Chinese date + fs = date_ptrn5.matcher(fs).replaceAll(""); + // If it starts with [$-...], then could be a date, but + // who knows what that starting bit is all about + fs = date_ptrn1.matcher(fs).replaceAll(""); + // If it starts with something like [Black] or [Yellow], + // then it could be a date + fs = date_ptrn2.matcher(fs).replaceAll(""); + // You're allowed something like dd/mm/yy;[red]dd/mm/yy + // which would place dates before 1900/1904 in red + // For now, only consider the first one + final int separatorIndex = fs.indexOf(';'); + if (0 < separatorIndex && separatorIndex < fs.length() - 1) { + fs = fs.substring(0, separatorIndex); + } + + // Ensure it has some date letters in it + // (Avoids false positives on the rest of pattern 3) + if (!date_ptrn3a.matcher(fs).find()) { + return false; + } + + // If we get here, check it's only made up, in any case, of: + // y m d h s - \ / , . : [ ] T + // optionally followed by AM/PM + boolean result = date_ptrn3b.matcher(fs).matches(); + if (result) { + return true; + } + result = date_ptrn6.matcher(fs).find(); + return result; + } + + /** + * Given a format ID this will check whether the format represents an internal excel date format or not. + * + * @see #isADateFormat(Integer, String) + */ + public static boolean isInternalDateFormat(int format) { + switch (format) { + // Internal Date Formats as described on page 427 in + // Microsoft Excel Dev's Kit... + // 14-22 + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + // 27-36 + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + // 45-47 + case 0x2d: + case 0x2e: + case 0x2f: + // 50-58 + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + return true; + } + return false; + } + + public static void removeThreadLocalCache() { + DATE_THREAD_LOCAL.remove(); + DATE_FORMAT_THREAD_LOCAL.remove(); } } diff --git a/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java b/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java new file mode 100644 index 0000000..5bceb4a --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.util; + +import com.alibaba.excel.metadata.DataFormatter; +import com.alibaba.excel.metadata.GlobalConfiguration; + +/** + * Convert number data, including date. + * + * @author Jiaju Zhuang + **/ +public class NumberDataFormatterUtils { + /** + * Cache DataFormatter. + */ + private static final ThreadLocal DATA_FORMATTER_THREAD_LOCAL = new ThreadLocal(); + + /** + * Format number data. + * + * @param data + * @param dataFormat + * Not null. + * @param dataFormatString + * @param globalConfiguration + * @return + */ + public static String format(Double data, Integer dataFormat, String dataFormatString, + GlobalConfiguration globalConfiguration) { + DataFormatter dataFormatter = DATA_FORMATTER_THREAD_LOCAL.get(); + if (dataFormatter == null) { + if (globalConfiguration != null) { + dataFormatter = + new DataFormatter(globalConfiguration.getLocale(), globalConfiguration.getUse1904windowing()); + } else { + dataFormatter = new DataFormatter(); + } + DATA_FORMATTER_THREAD_LOCAL.set(dataFormatter); + } + return dataFormatter.format(data, dataFormat, dataFormatString); + + } + + public static void removeThreadLocalCache() { + DATA_FORMATTER_THREAD_LOCAL.remove(); + } +} diff --git a/src/main/java/com/alibaba/excel/util/PositionUtils.java b/src/main/java/com/alibaba/excel/util/PositionUtils.java index b85018f..a527ae1 100644 --- a/src/main/java/com/alibaba/excel/util/PositionUtils.java +++ b/src/main/java/com/alibaba/excel/util/PositionUtils.java @@ -14,12 +14,16 @@ public class PositionUtils { } return row; } - public static int getRowByRowTagt(String rowTagt,int before) { - int row ; + + public static int getRowByRowTagt(String rowTagt, Integer before) { + int row; if (rowTagt != null) { row = Integer.parseInt(rowTagt) - 1; return row; - }else { + } else { + if (before == null) { + before = -1; + } return before + 1; } @@ -46,7 +50,7 @@ public class PositionUtils { return col - 1; } - public static int getCol(String currentCellIndex,int before) { + public static int getCol(String currentCellIndex, Integer before) { int col = 0; if (currentCellIndex != null) { @@ -55,8 +59,11 @@ public class PositionUtils { col += (currentIndex[i] - '@') * Math.pow(26, (currentIndex.length - i - 1)); } return col - 1; - }else { - return before +1; + } else { + if (before == null) { + before = -1; + } + return before + 1; } } diff --git a/src/main/java/com/alibaba/excel/util/SheetUtils.java b/src/main/java/com/alibaba/excel/util/SheetUtils.java index 66a1c48..c2c727c 100644 --- a/src/main/java/com/alibaba/excel/util/SheetUtils.java +++ b/src/main/java/com/alibaba/excel/util/SheetUtils.java @@ -1,12 +1,11 @@ package com.alibaba.excel.util; -import java.util.List; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; /** * Sheet utils @@ -23,17 +22,15 @@ public class SheetUtils { * * @param readSheet * actual sheet - * @param parameterReadSheetList - * parameters - * @param readAll + * @param analysisContext * @return */ - public static ReadSheet match(ReadSheet readSheet, List parameterReadSheetList, Boolean readAll, - GlobalConfiguration globalConfiguration) { - if (readAll) { + public static ReadSheet match(ReadSheet readSheet, AnalysisContext analysisContext) { + ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + if (readWorkbookHolder.getReadAll()) { return readSheet; } - for (ReadSheet parameterReadSheet : parameterReadSheetList) { + for (ReadSheet parameterReadSheet : readWorkbookHolder.getParameterSheetDataList()) { if (parameterReadSheet == null) { continue; } @@ -49,7 +46,8 @@ public class SheetUtils { String parameterSheetName = parameterReadSheet.getSheetName(); if (!StringUtils.isEmpty(parameterSheetName)) { boolean autoTrim = (parameterReadSheet.getAutoTrim() != null && parameterReadSheet.getAutoTrim()) - || (parameterReadSheet.getAutoTrim() == null && globalConfiguration.getAutoTrim()); + || (parameterReadSheet.getAutoTrim() == null + && analysisContext.readWorkbookHolder().getGlobalConfiguration().getAutoTrim()); if (autoTrim) { parameterSheetName = parameterSheetName.trim(); } diff --git a/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java index b303a31..06dcc72 100644 --- a/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java +++ b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java @@ -123,6 +123,21 @@ public class WriteHandlerUtils { } } + public static void afterCellDataConverted(WriteContext writeContext, CellData cellData, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellDataConverted(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cellData, cell, head, relativeRowIndex, isHead); + } + } + } + public static void afterCellDispose(WriteContext writeContext, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { List cellDataList = new ArrayList(); diff --git a/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java b/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java new file mode 100644 index 0000000..d44c0ad --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java @@ -0,0 +1,104 @@ +package com.alibaba.excel.write.builder; + +import java.util.ArrayList; +import java.util.Collection; + +import com.alibaba.excel.metadata.AbstractParameterBuilder; +import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.metadata.WriteBasicParameter; + +/** + * Build ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriterParameterBuilder extends AbstractParameterBuilder { + /** + * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. + * + * @param relativeHeadRowIndex + * @return + */ + public T relativeHeadRowIndex(Integer relativeHeadRowIndex) { + parameter().setRelativeHeadRowIndex(relativeHeadRowIndex); + return self(); + } + + /** + * Need Head + */ + public T needHead(Boolean needHead) { + parameter().setNeedHead(needHead); + return self(); + } + + /** + * Custom write handler + * + * @param writeHandler + * @return + */ + public T registerWriteHandler(WriteHandler writeHandler) { + if (parameter().getCustomWriteHandlerList() == null) { + parameter().setCustomWriteHandlerList(new ArrayList()); + } + parameter().getCustomWriteHandlerList().add(writeHandler); + return self(); + } + + /** + * Use the default style.Default is true. + * + * @param useDefaultStyle + * @return + */ + public T useDefaultStyle(Boolean useDefaultStyle) { + parameter().setUseDefaultStyle(useDefaultStyle); + return self(); + } + + /** + * Whether to automatically merge headers.Default is true. + * + * @param automaticMergeHead + * @return + */ + public T automaticMergeHead(Boolean automaticMergeHead) { + parameter().setAutomaticMergeHead(automaticMergeHead); + return self(); + } + + /** + * Ignore the custom columns. + */ + public T excludeColumnIndexes(Collection excludeColumnIndexes) { + parameter().setExcludeColumnIndexes(excludeColumnIndexes); + return self(); + } + + /** + * Ignore the custom columns. + */ + public T excludeColumnFiledNames(Collection excludeColumnFiledNames) { + parameter().setExcludeColumnFiledNames(excludeColumnFiledNames); + return self(); + } + + /** + * Only output the custom columns. + */ + public T includeColumnIndexes(Collection includeColumnIndexes) { + parameter().setIncludeColumnIndexes(includeColumnIndexes); + return self(); + } + + /** + * Only output the custom columns. + */ + public T includeColumnFiledNames(Collection includeColumnFiledNames) { + parameter().setIncludeColumnFiledNames(includeColumnFiledNames); + return self(); + } + +} diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java index c1f2d94..12c2f22 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -3,12 +3,8 @@ package com.alibaba.excel.write.builder; import java.io.File; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.converters.Converter; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteWorkbook; @@ -18,7 +14,7 @@ import com.alibaba.excel.write.metadata.WriteWorkbook; * * @author Jiaju Zhuang */ -public class ExcelWriterBuilder { +public class ExcelWriterBuilder extends AbstractExcelWriterParameterBuilder { /** * Workbook */ @@ -28,47 +24,6 @@ public class ExcelWriterBuilder { this.writeWorkbook = new WriteWorkbook(); } - /** - * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. - * - * @param relativeHeadRowIndex - * @return - */ - public ExcelWriterBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) { - writeWorkbook.setRelativeHeadRowIndex(relativeHeadRowIndex); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterBuilder#head(List)} and {@link ExcelWriterBuilder#head(Class)} - * - * @param head - * @return - */ - public ExcelWriterBuilder head(List> head) { - writeWorkbook.setHead(head); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterBuilder#head(List)} and {@link ExcelWriterBuilder#head(Class)} - * - * @param clazz - * @return - */ - public ExcelWriterBuilder head(Class clazz) { - writeWorkbook.setClazz(clazz); - return this; - } - - /** - * Need Head - */ - public ExcelWriterBuilder needHead(Boolean needHead) { - writeWorkbook.setNeedHead(needHead); - return this; - } - /** * Default true * @@ -80,28 +35,6 @@ public class ExcelWriterBuilder { return this; } - /** - * Use the default style.Default is true. - * - * @param useDefaultStyle - * @return - */ - public ExcelWriterBuilder useDefaultStyle(Boolean useDefaultStyle) { - writeWorkbook.setUseDefaultStyle(useDefaultStyle); - return this; - } - - /** - * Whether to automatically merge headers.Default is true. - * - * @param automaticMergeHead - * @return - */ - public ExcelWriterBuilder automaticMergeHead(Boolean automaticMergeHead) { - writeWorkbook.setAutomaticMergeHead(automaticMergeHead); - return this; - } - /** * Whether the encryption. *

@@ -125,38 +58,6 @@ public class ExcelWriterBuilder { return this; } - /** - * Ignore the custom columns. - */ - public ExcelWriterBuilder excludeColumnIndexes(Collection excludeColumnIndexes) { - writeWorkbook.setExcludeColumnIndexes(excludeColumnIndexes); - return this; - } - - /** - * Ignore the custom columns. - */ - public ExcelWriterBuilder excludeColumnFiledNames(Collection excludeColumnFiledNames) { - writeWorkbook.setExcludeColumnFiledNames(excludeColumnFiledNames); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterBuilder includeColumnIndexes(Collection includeColumnIndexes) { - writeWorkbook.setIncludeColumnIndexes(includeColumnIndexes); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterBuilder includeColumnFiledNames(Collection includeColumnFiledNames) { - writeWorkbook.setIncludeColumnFiledNames(includeColumnFiledNames); - return this; - } - /** * Excel is also written in the event of an exception being thrown.The default false. */ @@ -181,34 +82,6 @@ public class ExcelWriterBuilder { return this; } - /** - * Custom type conversions override the default. - * - * @param converter - * @return - */ - public ExcelWriterBuilder registerConverter(Converter converter) { - if (writeWorkbook.getCustomConverterList() == null) { - writeWorkbook.setCustomConverterList(new ArrayList()); - } - writeWorkbook.getCustomConverterList().add(converter); - return this; - } - - /** - * Custom write handler - * - * @param writeHandler - * @return - */ - public ExcelWriterBuilder registerWriteHandler(WriteHandler writeHandler) { - if (writeWorkbook.getCustomWriteHandlerList() == null) { - writeWorkbook.setCustomWriteHandlerList(new ArrayList()); - } - writeWorkbook.getCustomWriteHandlerList().add(writeHandler); - return this; - } - public ExcelWriterBuilder excelType(ExcelTypeEnum excelType) { writeWorkbook.setExcelType(excelType); return this; @@ -281,4 +154,8 @@ public class ExcelWriterBuilder { return excelWriterSheetBuilder; } + @Override + protected WriteWorkbook parameter() { + return writeWorkbook; + } } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java index 5cb71cc..f79b771 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -1,13 +1,9 @@ package com.alibaba.excel.write.builder; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.converters.Converter; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; @@ -16,7 +12,7 @@ import com.alibaba.excel.write.metadata.fill.FillConfig; * * @author Jiaju Zhuang */ -public class ExcelWriterSheetBuilder { +public class ExcelWriterSheetBuilder extends AbstractExcelWriterParameterBuilder { private ExcelWriter excelWriter; /** * Sheet @@ -32,99 +28,6 @@ public class ExcelWriterSheetBuilder { this.excelWriter = excelWriter; } - /** - * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. - * - * @param relativeHeadRowIndex - * @return - */ - public ExcelWriterSheetBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) { - writeSheet.setRelativeHeadRowIndex(relativeHeadRowIndex); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterSheetBuilder#head(List)} and - * {@link ExcelWriterSheetBuilder#head(Class)} - * - * @param head - * @return - */ - public ExcelWriterSheetBuilder head(List> head) { - writeSheet.setHead(head); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterSheetBuilder#head(List)} and - * {@link ExcelWriterSheetBuilder#head(Class)} - * - * @param clazz - * @return - */ - public ExcelWriterSheetBuilder head(Class clazz) { - writeSheet.setClazz(clazz); - return this; - } - - /** - * Need Head - */ - public ExcelWriterSheetBuilder needHead(Boolean needHead) { - writeSheet.setNeedHead(needHead); - return this; - } - - /** - * Use the default style.Default is true. - * - * @param useDefaultStyle - * @return - */ - public ExcelWriterSheetBuilder useDefaultStyle(Boolean useDefaultStyle) { - writeSheet.setUseDefaultStyle(useDefaultStyle); - return this; - } - - /** - * Whether to automatically merge headers.Default is true. - * - * @param automaticMergeHead - * @return - */ - public ExcelWriterSheetBuilder automaticMergeHead(Boolean automaticMergeHead) { - writeSheet.setAutomaticMergeHead(automaticMergeHead); - return this; - } - - /** - * Custom type conversions override the default. - * - * @param converter - * @return - */ - public ExcelWriterSheetBuilder registerConverter(Converter converter) { - if (writeSheet.getCustomConverterList() == null) { - writeSheet.setCustomConverterList(new ArrayList()); - } - writeSheet.getCustomConverterList().add(converter); - return this; - } - - /** - * Custom write handler - * - * @param writeHandler - * @return - */ - public ExcelWriterSheetBuilder registerWriteHandler(WriteHandler writeHandler) { - if (writeSheet.getCustomWriteHandlerList() == null) { - writeSheet.setCustomWriteHandlerList(new ArrayList()); - } - writeSheet.getCustomWriteHandlerList().add(writeHandler); - return this; - } - /** * Starting from 0 * @@ -147,38 +50,6 @@ public class ExcelWriterSheetBuilder { return this; } - /** - * Ignore the custom columns. - */ - public ExcelWriterSheetBuilder excludeColumnIndexes(Collection excludeColumnIndexes) { - writeSheet.setExcludeColumnIndexes(excludeColumnIndexes); - return this; - } - - /** - * Ignore the custom columns. - */ - public ExcelWriterSheetBuilder excludeColumnFiledNames(Collection excludeColumnFiledNames) { - writeSheet.setExcludeColumnFiledNames(excludeColumnFiledNames); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterSheetBuilder includeColumnIndexes(Collection includeColumnIndexes) { - writeSheet.setIncludeColumnIndexes(includeColumnIndexes); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterSheetBuilder includeColumnFiledNames(Collection includeColumnFiledNames) { - writeSheet.setIncludeColumnFiledNames(includeColumnFiledNames); - return this; - } - public WriteSheet build() { return writeSheet; } @@ -215,4 +86,9 @@ public class ExcelWriterSheetBuilder { return excelWriterTableBuilder; } + @Override + protected WriteSheet parameter() { + return writeSheet; + } + } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java index 2b461a9..077361a 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java @@ -1,13 +1,9 @@ package com.alibaba.excel.write.builder; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.converters.Converter; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; @@ -16,7 +12,7 @@ import com.alibaba.excel.write.metadata.WriteTable; * * @author Jiaju Zhuang */ -public class ExcelWriterTableBuilder { +public class ExcelWriterTableBuilder extends AbstractExcelWriterParameterBuilder { private ExcelWriter excelWriter; @@ -36,99 +32,6 @@ public class ExcelWriterTableBuilder { this.writeTable = new WriteTable(); } - /** - * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. - * - * @param relativeHeadRowIndex - * @return - */ - public ExcelWriterTableBuilder relativeHeadRowIndex(Integer relativeHeadRowIndex) { - writeTable.setRelativeHeadRowIndex(relativeHeadRowIndex); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterTableBuilder#head(List)} and - * {@link ExcelWriterTableBuilder#head(Class)} - * - * @param head - * @return - */ - public ExcelWriterTableBuilder head(List> head) { - writeTable.setHead(head); - return this; - } - - /** - * You can only choose one of the {@link ExcelWriterTableBuilder#head(List)} and - * {@link ExcelWriterTableBuilder#head(Class)} - * - * @param clazz - * @return - */ - public ExcelWriterTableBuilder head(Class clazz) { - writeTable.setClazz(clazz); - return this; - } - - /** - * Need Head - */ - public ExcelWriterTableBuilder needHead(Boolean needHead) { - writeTable.setNeedHead(needHead); - return this; - } - - /** - * Use the default style.Default is true. - * - * @param useDefaultStyle - * @return - */ - public ExcelWriterTableBuilder useDefaultStyle(Boolean useDefaultStyle) { - writeTable.setUseDefaultStyle(useDefaultStyle); - return this; - } - - /** - * Whether to automatically merge headers.Default is true. - * - * @param automaticMergeHead - * @return - */ - public ExcelWriterTableBuilder automaticMergeHead(Boolean automaticMergeHead) { - writeTable.setAutomaticMergeHead(automaticMergeHead); - return this; - } - - /** - * Custom type conversions override the default. - * - * @param converter - * @return - */ - public ExcelWriterTableBuilder registerConverter(Converter converter) { - if (writeTable.getCustomConverterList() == null) { - writeTable.setCustomConverterList(new ArrayList()); - } - writeTable.getCustomConverterList().add(converter); - return this; - } - - /** - * Custom write handler - * - * @param writeHandler - * @return - */ - public ExcelWriterTableBuilder registerWriteHandler(WriteHandler writeHandler) { - if (writeTable.getCustomWriteHandlerList() == null) { - writeTable.setCustomWriteHandlerList(new ArrayList()); - } - writeTable.getCustomWriteHandlerList().add(writeHandler); - return this; - } - /** * Starting from 0 * @@ -140,38 +43,6 @@ public class ExcelWriterTableBuilder { return this; } - /** - * Ignore the custom columns. - */ - public ExcelWriterTableBuilder excludeColumnIndexes(Collection excludeColumnIndexes) { - writeTable.setExcludeColumnIndexes(excludeColumnIndexes); - return this; - } - - /** - * Ignore the custom columns. - */ - public ExcelWriterTableBuilder excludeColumnFiledNames(Collection excludeColumnFiledNames) { - writeTable.setExcludeColumnFiledNames(excludeColumnFiledNames); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterTableBuilder includeColumnIndexes(Collection includeColumnIndexes) { - writeTable.setIncludeColumnIndexes(includeColumnIndexes); - return this; - } - - /** - * Only output the custom columns. - */ - public ExcelWriterTableBuilder includeColumnFiledNames(Collection includeColumnFiledNames) { - writeSheet.setIncludeColumnFiledNames(includeColumnFiledNames); - return this; - } - public WriteTable build() { return writeTable; } @@ -184,4 +55,8 @@ public class ExcelWriterTableBuilder { excelWriter.finish(); } + @Override + protected WriteTable parameter() { + return writeTable; + } } diff --git a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java index 1f09d47..4eb115c 100644 --- a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java @@ -13,7 +13,9 @@ import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.holder.WriteHolder; /** @@ -29,7 +31,7 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { } protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { + ExcelContentProperty excelContentProperty, Head head, Integer relativeRowIndex) { if (value == null) { return new CellData(CellDataTypeEnum.EMPTY); } @@ -43,6 +45,7 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { if (cellData.getType() == null) { cellData.setType(CellDataTypeEnum.EMPTY); } + WriteHandlerUtils.afterCellDataConverted(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); switch (cellData.getType()) { case STRING: cell.setCellValue(cellData.getStringValue()); diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java index 5b0d8ae..e218fa7 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java @@ -37,7 +37,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { public void add(List data) { if (CollectionUtils.isEmpty(data)) { - return; + data = new ArrayList(); } WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); @@ -105,7 +105,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); Object value = oneRowData.get(dataIndex); CellData cellData = converterAndSet(writeContext.currentWriteHolder(), value == null ? null : value.getClass(), - cell, value, null); + cell, value, null, head, relativeRowIndex); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); } @@ -135,7 +135,7 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); Object value = beanMap.get(name); CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, - value, excelContentProperty); + value, excelContentProperty, head, relativeRowIndex); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); beanMapHandledSet.add(name); } @@ -160,8 +160,8 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); Cell cell = WorkBookUtil.createCell(row, cellIndex++); WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); - CellData cellData = - converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell, value, null); + CellData cellData = converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell, + value, null, null, relativeRowIndex); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); } } diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index 495a134..51d7d3a 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -27,6 +27,7 @@ import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.fill.AnalysisCell; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import net.sf.cglib.beans.BeanMap; @@ -47,41 +48,65 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { /** * Fields to replace in the template */ - private Map> templateAnalysisCache = new HashMap>(8); + private Map> templateAnalysisCache = new HashMap>(8); /** * Collection fields to replace in the template */ - private Map> templateCollectionAnalysisCache = - new HashMap>(8); + private Map> templateCollectionAnalysisCache = + new HashMap>(8); /** * Style cache for collection fields */ - private Map> collectionFieldStyleCache = - new HashMap>(8); + private Map> collectionFieldStyleCache = + new HashMap>(8); /** * Row height cache for collection */ - private Map collectionRowHeightCache = new HashMap(8); + private Map collectionRowHeightCache = new HashMap(8); /** * Last index cache for collection fields */ - private Map> collectionLastIndexCache = - new HashMap>(8); + private Map> collectionLastIndexCache = + new HashMap>(8); - private Map relativeRowIndexMap = new HashMap(8); + private Map relativeRowIndexMap = new HashMap(8); + /** + * The data prefix that is populated this time + */ + private String currentDataPrefix; + /** + * The unique data encoding for this fill + */ + private String currentUniqueDataFlag; public ExcelWriteFillExecutor(WriteContext writeContext) { super(writeContext); } public void fill(Object data, FillConfig fillConfig) { + if (data == null) { + data = new HashMap(16); + } if (fillConfig == null) { fillConfig = FillConfig.builder().build(true); } fillConfig.init(); - if (data instanceof Collection) { + + Object realData; + if (data instanceof FillWrapper) { + FillWrapper fillWrapper = (FillWrapper)data; + currentDataPrefix = fillWrapper.getName(); + realData = fillWrapper.getCollectionData(); + } else { + realData = data; + currentDataPrefix = null; + } + currentUniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), currentDataPrefix); + + // processing data + if (realData instanceof Collection) { List analysisCellList = readTemplateData(templateCollectionAnalysisCache); - Collection collectionData = (Collection)data; + Collection collectionData = (Collection)realData; if (CollectionUtils.isEmpty(collectionData)) { return; } @@ -93,7 +118,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); } } else { - doFill(readTemplateData(templateAnalysisCache), data, fillConfig, null); + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); } } @@ -102,8 +127,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return; } int maxRowIndex = 0; - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); for (AnalysisCell analysisCell : analysisCellList) { if (collectionLastIndexMap != null) { Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); @@ -132,7 +156,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return; } sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false); - for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { + for (AnalysisCell analysisCell : templateAnalysisCache.get(currentUniqueDataFlag)) { if (analysisCell.getRowIndex() > maxRowIndex) { analysisCell.setRowIndex(analysisCell.getRowIndex() + number); } @@ -162,7 +186,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } Object value = dataMap.get(variable); CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, - value, fieldNameContentPropertyMap.get(variable)); + value, fieldNameContentPropertyMap.get(variable), null, relativeRowIndex); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); } else { StringBuilder cellValueBuild = new StringBuilder(); @@ -206,14 +230,13 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } private Integer getRelativeRowIndex() { - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - Integer relativeRowIndex = relativeRowIndexMap.get(sheetNo); + Integer relativeRowIndex = relativeRowIndexMap.get(currentUniqueDataFlag); if (relativeRowIndex == null) { relativeRowIndex = 0; } else { relativeRowIndex++; } - relativeRowIndexMap.put(sheetNo, relativeRowIndex); + relativeRowIndexMap.put(currentUniqueDataFlag, relativeRowIndex); return relativeRowIndex; } @@ -222,13 +245,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); } - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); Sheet sheet = writeContext.writeSheetHolder().getSheet(); - Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); if (collectionLastIndexMap == null) { collectionLastIndexMap = new HashMap(16); - collectionLastIndexCache.put(sheetNo, collectionLastIndexMap); + collectionLastIndexCache.put(currentUniqueDataFlag, collectionLastIndexMap); } boolean isOriginalCell = false; Integer lastRowIndex; @@ -269,10 +291,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } else { row = sheet.createRow(lastRowIndex); } - checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo); + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); } else { - checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo); + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); } } Cell cell = row.getCell(lastColumnIndex); @@ -282,10 +304,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); } - Map collectionFieldStyleMap = collectionFieldStyleCache.get(sheetNo); + Map collectionFieldStyleMap = collectionFieldStyleCache.get(currentUniqueDataFlag); if (collectionFieldStyleMap == null) { collectionFieldStyleMap = new HashMap(16); - collectionFieldStyleCache.put(sheetNo, collectionFieldStyleMap); + collectionFieldStyleCache.put(currentUniqueDataFlag, collectionFieldStyleMap); } if (isOriginalCell) { collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); @@ -298,31 +320,27 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return cell; } - private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row, - Integer sheetNo) { - if (!analysisCell.getFirstColumn() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { + private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row) { + if (!analysisCell.getFirstRow() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { return; } if (isOriginalCell) { - collectionRowHeightCache.put(sheetNo, row.getHeight()); + collectionRowHeightCache.put(currentUniqueDataFlag, row.getHeight()); return; } - Short rowHeight = collectionRowHeightCache.get(sheetNo); + Short rowHeight = collectionRowHeightCache.get(currentUniqueDataFlag); if (rowHeight != null) { row.setHeight(rowHeight); } } - private List readTemplateData(Map> analysisCache) { - Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); - List analysisCellList = analysisCache.get(sheetNo); + private List readTemplateData(Map> analysisCache) { + List analysisCellList = analysisCache.get(currentUniqueDataFlag); if (analysisCellList != null) { return analysisCellList; } Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); - analysisCellList = new ArrayList(); - List collectionAnalysisCellList = new ArrayList(); - Set firstColumnCache = new HashSet(); + Map> firstRowCache = new HashMap>(8); for (int i = 0; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row == null) { @@ -333,32 +351,26 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (cell == null) { continue; } - String preparedData = - prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j, firstColumnCache); + String preparedData = prepareData(cell, i, j, firstRowCache); // Prevent empty data from not being replaced if (preparedData != null) { cell.setCellValue(preparedData); } } } - templateAnalysisCache.put(sheetNo, analysisCellList); - templateCollectionAnalysisCache.put(sheetNo, collectionAnalysisCellList); - return analysisCache.get(sheetNo); + return analysisCache.get(currentUniqueDataFlag); } /** * To prepare data * * @param cell - * @param analysisCellList - * @param collectionAnalysisCellList * @param rowIndex * @param columnIndex - * @param firstColumnCache + * @param firstRowCache * @return Returns the data that the cell needs to replace */ - private String prepareData(Cell cell, List analysisCellList, - List collectionAnalysisCellList, int rowIndex, int columnIndex, Set firstColumnCache) { + private String prepareData(Cell cell, int rowIndex, int columnIndex, Map> firstRowCache) { if (!CellType.STRING.equals(cell.getCellTypeEnum())) { return null; } @@ -404,8 +416,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { if (StringUtils.isEmpty(variable)) { continue; } - if (variable.startsWith(COLLECTION_PREFIX)) { - variable = variable.substring(1); + int collectPrefixIndex = variable.indexOf(COLLECTION_PREFIX); + if (collectPrefixIndex > -1) { + if (collectPrefixIndex != 0) { + analysisCell.setPrefix(variable.substring(0, collectPrefixIndex)); + } + variable = variable.substring(collectPrefixIndex + 1); if (StringUtils.isEmpty(variable)) { continue; } @@ -422,13 +438,12 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } lastPrepareDataIndex = suffixIndex + 1; } - return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, analysisCellList, - collectionAnalysisCellList, firstColumnCache, preparedData); + return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, firstRowCache, + preparedData); } private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex, - int length, List analysisCellList, List collectionAnalysisCellList, - Set firstColumnCache, StringBuilder preparedData) { + int length, Map> firstRowCache, StringBuilder preparedData) { if (analysisCell != null) { if (lastPrepareDataIndex == length) { analysisCell.getPrepareDataList().add(StringUtils.EMPTY); @@ -436,12 +451,29 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); analysisCell.setOnlyOneVariable(Boolean.FALSE); } + String uniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), analysisCell.getPrefix()); if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + List analysisCellList = templateAnalysisCache.get(uniqueDataFlag); + if (analysisCellList == null) { + analysisCellList = new ArrayList(); + templateAnalysisCache.put(uniqueDataFlag, analysisCellList); + } analysisCellList.add(analysisCell); } else { - if (!firstColumnCache.contains(rowIndex)) { - analysisCell.setFirstColumn(Boolean.TRUE); - firstColumnCache.add(rowIndex); + Set uniqueFirstRowCache = firstRowCache.get(uniqueDataFlag); + if (uniqueFirstRowCache == null) { + uniqueFirstRowCache = new HashSet(); + firstRowCache.put(uniqueDataFlag, uniqueFirstRowCache); + } + if (!uniqueFirstRowCache.contains(rowIndex)) { + analysisCell.setFirstRow(Boolean.TRUE); + uniqueFirstRowCache.add(rowIndex); + } + + List collectionAnalysisCellList = templateCollectionAnalysisCache.get(uniqueDataFlag); + if (collectionAnalysisCellList == null) { + collectionAnalysisCellList = new ArrayList(); + templateCollectionAnalysisCache.put(uniqueDataFlag, collectionAnalysisCellList); } collectionAnalysisCellList.add(analysisCell); } @@ -460,7 +492,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { List prepareDataList = new ArrayList(); analysisCell.setPrepareDataList(prepareDataList); analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); - analysisCell.setFirstColumn(Boolean.FALSE); + analysisCell.setFirstRow(Boolean.FALSE); return analysisCell; } @@ -470,4 +502,17 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { return prepareData; } + private String uniqueDataFlag(WriteSheetHolder writeSheetHolder, String wrapperName) { + String prefix; + if (writeSheetHolder.getSheetNo() != null) { + prefix = writeSheetHolder.getSheetNo().toString(); + } else { + prefix = writeSheetHolder.getSheetName().toString(); + } + if (StringUtils.isEmpty(wrapperName)) { + return prefix + "-"; + } + return prefix + "-" + wrapperName; + } + } diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java similarity index 58% rename from src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java rename to src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java index b661c12..b649af7 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java @@ -1,23 +1,22 @@ -package com.alibaba.excel.write.merge; +package com.alibaba.excel.write.handler; import java.util.List; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; /** - * Merge strategy + * Abstract cell write handler * * @author Jiaju Zhuang - */ -public abstract class AbstractMergeStrategy implements CellWriteHandler { + **/ +public abstract class AbstractCellWriteHandler implements CellWriteHandler { + @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { @@ -26,24 +25,19 @@ public abstract class AbstractMergeStrategy implements CellWriteHandler { @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, Integer relativeRowIndex, Boolean isHead) {} + Head head, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + + } @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { - if (isHead) { - return; - } - merge(writeSheetHolder.getSheet(), cell, head, relativeRowIndex); - } - /** - * merge - * - * @param sheet - * @param cell - * @param head - * @param relativeRowIndex - */ - protected abstract void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex); + } } diff --git a/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java new file mode 100644 index 0000000..d1e8c3a --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.write.handler; + +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +/** + * Abstract row write handler + * + * @author Jiaju Zhuang + **/ +public abstract class AbstractRowWriteHandler implements RowWriteHandler { + @Override + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + + } +} diff --git a/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java new file mode 100644 index 0000000..76f4c84 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * Abstract sheet write handler + * + * @author Jiaju Zhuang + **/ +public abstract class AbstractSheetWriteHandler implements SheetWriteHandler { + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } +} diff --git a/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java new file mode 100644 index 0000000..baad392 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * Abstract workbook write handler + * + * @author Jiaju Zhuang + **/ +public abstract class AbstractWorkbookWriteHandler implements WorkbookWriteHandler { + + @Override + public void beforeWorkbookCreate() { + + } + + @Override + public void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) { + + } + + @Override + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { + + } +} diff --git a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java index 4d98138..8701d6a 100644 --- a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -52,6 +52,25 @@ public interface CellWriteHandler extends WriteHandler { void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead); + /** + * Called after the cell data is converted + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param cell + * @param head + * Nullable.It is null in the case of fill data and without head. + * @param cellData + * Nullable.It is null in the case of add header. + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * It will always be false when fill data. + */ + void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, + Cell cell, Head head, Integer relativeRowIndex, Boolean isHead); + /** * Called after all operations on the cell have been completed * diff --git a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java index 615e408..5796dde 100644 --- a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java @@ -1,58 +1,69 @@ package com.alibaba.excel.write.merge; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellRangeAddress; -import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.write.handler.AbstractRowWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; /** * The regions of the loop merge * * @author Jiaju Zhuang */ -public class LoopMergeStrategy extends AbstractMergeStrategy { +public class LoopMergeStrategy extends AbstractRowWriteHandler { + /** + * Each row + */ private int eachRow; - private int columnCount; + /** + * Extend column + */ + private int columnExtend; + /** + * The number of the current column + */ private int columnIndex; public LoopMergeStrategy(int eachRow, int columnIndex) { this(eachRow, 1, columnIndex); } - public LoopMergeStrategy(int eachRow, int columnCount, int columnIndex) { + public LoopMergeStrategy(int eachRow, int columnExtend, int columnIndex) { if (eachRow < 1) { throw new IllegalArgumentException("EachRows must be greater than 1"); } - if (columnCount < 1) { - throw new IllegalArgumentException("ColumnCount must be greater than 1"); + if (columnExtend < 1) { + throw new IllegalArgumentException("ColumnExtend must be greater than 1"); } - if (columnCount == 1 && eachRow == 1) { - throw new IllegalArgumentException("ColumnCount or eachRows must be greater than 1"); + if (columnExtend == 1 && eachRow == 1) { + throw new IllegalArgumentException("ColumnExtend or eachRows must be greater than 1"); } if (columnIndex < 0) { throw new IllegalArgumentException("ColumnIndex must be greater than 0"); } this.eachRow = eachRow; - this.columnCount = columnCount; + this.columnExtend = columnExtend; this.columnIndex = columnIndex; } + public LoopMergeStrategy(LoopMergeProperty loopMergeProperty, Integer columnIndex) { + this(loopMergeProperty.getEachRow(), loopMergeProperty.getColumnExtend(), columnIndex); + } + @Override - protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - if (relativeRowIndex == null) { + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead) { return; } - Integer currentColumnIndex; - if (head != null) { - currentColumnIndex = head.getColumnIndex(); - } else { - currentColumnIndex = cell.getColumnIndex(); - } - if (currentColumnIndex == columnIndex && relativeRowIndex % eachRow == 0) { - CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(), - cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex() + columnCount - 1); - sheet.addMergedRegionUnsafe(cellRangeAddress); + if (relativeRowIndex % eachRow == 0) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(row.getRowNum(), row.getRowNum() + eachRow - 1, + columnIndex, columnIndex + columnExtend - 1); + writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress); } } + } diff --git a/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java index be05e22..d85781e 100644 --- a/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java @@ -1,21 +1,33 @@ package com.alibaba.excel.write.merge; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; -import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; +import com.alibaba.excel.write.handler.AbstractSheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; /** * It only merges once when create cell(firstRowIndex,lastRowIndex) * * @author Jiaju Zhuang */ -public class OnceAbsoluteMergeStrategy extends AbstractMergeStrategy { - +public class OnceAbsoluteMergeStrategy extends AbstractSheetWriteHandler { + /** + * First row + */ private int firstRowIndex; + /** + * Last row + */ private int lastRowIndex; + /** + * First column + */ private int firstColumnIndex; + /** + * Last row + */ private int lastColumnIndex; public OnceAbsoluteMergeStrategy(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) { @@ -28,12 +40,15 @@ public class OnceAbsoluteMergeStrategy extends AbstractMergeStrategy { this.lastColumnIndex = lastColumnIndex; } + public OnceAbsoluteMergeStrategy(OnceAbsoluteMergeProperty onceAbsoluteMergeProperty) { + this(onceAbsoluteMergeProperty.getFirstRowIndex(), onceAbsoluteMergeProperty.getLastRowIndex(), + onceAbsoluteMergeProperty.getFirstColumnIndex(), onceAbsoluteMergeProperty.getLastColumnIndex()); + } + @Override - protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - if (cell.getRowIndex() == firstRowIndex && cell.getColumnIndex() == firstColumnIndex) { - CellRangeAddress cellRangeAddress = - new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex); - sheet.addMergedRegionUnsafe(cellRangeAddress); - } + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + CellRangeAddress cellRangeAddress = + new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex); + writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress); } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java index 47a2594..41b2cfc 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -16,7 +16,8 @@ public class AnalysisCell { private List prepareDataList; private Boolean onlyOneVariable; private WriteTemplateAnalysisCellTypeEnum cellType; - private Boolean firstColumn; + private String prefix; + private Boolean firstRow; public int getColumnIndex() { return columnIndex; @@ -58,6 +59,14 @@ public class AnalysisCell { this.onlyOneVariable = onlyOneVariable; } + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + public WriteTemplateAnalysisCellTypeEnum getCellType() { return cellType; } @@ -66,12 +75,12 @@ public class AnalysisCell { this.cellType = cellType; } - public Boolean getFirstColumn() { - return firstColumn; + public Boolean getFirstRow() { + return firstRow; } - public void setFirstColumn(Boolean firstColumn) { - this.firstColumn = firstColumn; + public void setFirstRow(Boolean firstRow) { + this.firstRow = firstRow; } @Override diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java b/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java new file mode 100644 index 0000000..e25e4da --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.Collection; + +/** + * Multiple lists are supported when packing + * + * @author Jiaju Zhuang + **/ +public class FillWrapper { + /** + * The collection prefix that needs to be filled. + */ + private String name; + /** + * Data that needs to be filled. + */ + private Collection collectionData; + + public FillWrapper(Collection collectionData) { + this.collectionData = collectionData; + } + + public FillWrapper(String name, Collection collectionData) { + this.name = name; + this.collectionData = collectionData; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getCollectionData() { + return collectionData; + } + + public void setCollectionData(Collection collectionData) { + this.collectionData = collectionData; + } +} diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index 375602a..00f2ef7 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -22,6 +22,8 @@ import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.Font; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.TableStyle; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; import com.alibaba.excel.metadata.property.RowHeightProperty; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.handler.CellWriteHandler; @@ -30,12 +32,15 @@ import com.alibaba.excel.write.handler.RowWriteHandler; import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.write.handler.WorkbookWriteHandler; import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteBasicParameter; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.property.ExcelWriteHeadProperty; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.AbstractHeadColumnWidthStyleStrategy; import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; @@ -279,19 +284,63 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ return; } Map headMap = getExcelWriteHeadProperty().getHeadMap(); - boolean hasColumnWidth = false; - for (Map.Entry entry : headMap.entrySet()) { - if (entry.getValue().getColumnWidthProperty() != null) { + boolean hasStyle = false; + + for (Head head : headMap.values()) { + if (head.getColumnWidthProperty() != null) { hasColumnWidth = true; - break; } + if (head.getHeadStyleProperty() != null || head.getHeadFontProperty() != null + || head.getContentStyleProperty() != null || head.getContentFontProperty() != null) { + hasStyle = true; + } + dealLoopMerge(handlerList, head); } if (hasColumnWidth) { dealColumnWidth(handlerList); } + + if (hasStyle) { + dealStyle(handlerList); + } + dealRowHigh(handlerList); + dealOnceAbsoluteMerge(handlerList); + + } + + private void dealStyle(List handlerList) { + WriteHandler styleStrategy = new AbstractVerticalCellStyleStrategy() { + @Override + protected WriteCellStyle headCellStyle(Head head) { + return WriteCellStyle.build(head.getHeadStyleProperty(), head.getHeadFontProperty()); + } + + @Override + protected WriteCellStyle contentCellStyle(Head head) { + return WriteCellStyle.build(head.getContentStyleProperty(), head.getContentFontProperty()); + } + }; + handlerList.add(styleStrategy); + } + + private void dealLoopMerge(List handlerList, Head head) { + LoopMergeProperty loopMergeProperty = head.getLoopMergeProperty(); + if (loopMergeProperty == null) { + return; + } + handlerList.add(new LoopMergeStrategy(loopMergeProperty, head.getColumnIndex())); + } + + private void dealOnceAbsoluteMerge(List handlerList) { + OnceAbsoluteMergeProperty onceAbsoluteMergeProperty = + getExcelWriteHeadProperty().getOnceAbsoluteMergeProperty(); + if (onceAbsoluteMergeProperty == null) { + return; + } + handlerList.add(new OnceAbsoluteMergeStrategy(onceAbsoluteMergeProperty)); } private void dealRowHigh(List handlerList) { diff --git a/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java b/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java index 01786ce..81c85e8 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java +++ b/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java @@ -8,6 +8,10 @@ import org.apache.poi.ss.usermodel.IgnoredErrorType; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.VerticalAlignment; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.StyleProperty; +import com.alibaba.excel.util.StringUtils; + /** * Cell style when writing * @@ -137,6 +141,79 @@ public class WriteCellStyle { */ private Boolean shrinkToFit; + public static WriteCellStyle build(StyleProperty styleProperty, FontProperty fontProperty) { + if (styleProperty == null && fontProperty == null) { + return null; + } + WriteCellStyle writeCellStyle = new WriteCellStyle(); + if (styleProperty != null) { + if (styleProperty.getDataFormat() >= 0) { + writeCellStyle.setDataFormat(styleProperty.getDataFormat()); + } + writeCellStyle.setHidden(styleProperty.getHidden()); + writeCellStyle.setLocked(styleProperty.getLocked()); + writeCellStyle.setQuotePrefix(styleProperty.getQuotePrefix()); + writeCellStyle.setHorizontalAlignment(styleProperty.getHorizontalAlignment()); + writeCellStyle.setWrapped(styleProperty.getWrapped()); + writeCellStyle.setVerticalAlignment(styleProperty.getVerticalAlignment()); + if (styleProperty.getRotation() >= 0) { + writeCellStyle.setRotation(styleProperty.getRotation()); + } + if (styleProperty.getIndent() >= 0) { + writeCellStyle.setIndent(styleProperty.getIndent()); + } + writeCellStyle.setBorderLeft(styleProperty.getBorderLeft()); + writeCellStyle.setBorderRight(styleProperty.getBorderRight()); + writeCellStyle.setBorderTop(styleProperty.getBorderTop()); + writeCellStyle.setBorderBottom(styleProperty.getBorderBottom()); + if (styleProperty.getLeftBorderColor() >= 0) { + writeCellStyle.setLeftBorderColor(styleProperty.getLeftBorderColor()); + } + if (styleProperty.getRightBorderColor() >= 0) { + writeCellStyle.setRightBorderColor(styleProperty.getRightBorderColor()); + } + if (styleProperty.getTopBorderColor() >= 0) { + writeCellStyle.setTopBorderColor(styleProperty.getTopBorderColor()); + } + if (styleProperty.getBottomBorderColor() >= 0) { + writeCellStyle.setBottomBorderColor(styleProperty.getBottomBorderColor()); + } + writeCellStyle.setFillPatternType(styleProperty.getFillPatternType()); + if (styleProperty.getFillBackgroundColor() >= 0) { + writeCellStyle.setFillBackgroundColor(styleProperty.getFillBackgroundColor()); + } + if (styleProperty.getFillForegroundColor() >= 0) { + writeCellStyle.setFillForegroundColor(styleProperty.getFillForegroundColor()); + } + writeCellStyle.setShrinkToFit(styleProperty.getShrinkToFit()); + } + if (fontProperty != null) { + WriteFont writeFont = new WriteFont(); + writeCellStyle.setWriteFont(writeFont); + if (!StringUtils.isEmpty(fontProperty.getFontName())) { + writeFont.setFontName(fontProperty.getFontName()); + } + writeFont.setFontHeightInPoints(fontProperty.getFontHeightInPoints()); + writeFont.setItalic(fontProperty.getItalic()); + writeFont.setStrikeout(fontProperty.getStrikeout()); + if (fontProperty.getColor() >= 0) { + writeFont.setColor(fontProperty.getColor()); + } + if (fontProperty.getTypeOffset() >= 0) { + writeFont.setTypeOffset(fontProperty.getTypeOffset()); + } + if (fontProperty.getUnderline() >= 0) { + writeFont.setUnderline(fontProperty.getUnderline()); + } + if (fontProperty.getCharset() >= 0) { + writeFont.setCharset(fontProperty.getCharset()); + } + writeFont.setBold(fontProperty.getBold()); + } + + return writeCellStyle; + } + public Short getDataFormat() { return dataFormat; } diff --git a/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java b/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java index b1ba891..8fe0349 100644 --- a/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java +++ b/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java @@ -9,8 +9,14 @@ import java.util.Set; import com.alibaba.excel.annotation.format.NumberFormat; import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.annotation.write.style.OnceAbsoluteMerge; import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.converters.DefaultConverterLoader; import com.alibaba.excel.enums.CellDataTypeEnum; @@ -21,7 +27,11 @@ import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.property.ColumnWidthProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelHeadProperty; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; import com.alibaba.excel.metadata.property.RowHeightProperty; +import com.alibaba.excel.metadata.property.StyleProperty; /** * Define the header attribute of excel @@ -31,6 +41,7 @@ import com.alibaba.excel.metadata.property.RowHeightProperty; public class ExcelWriteHeadProperty extends ExcelHeadProperty { private RowHeightProperty headRowHeightProperty; private RowHeightProperty contentRowHeightProperty; + private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty; public ExcelWriteHeadProperty(Holder holder, Class headClazz, List> head, Boolean convertAllFiled) { super(holder, headClazz, head, convertAllFiled); @@ -41,8 +52,15 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty { RowHeightProperty.build((HeadRowHeight)headClazz.getAnnotation(HeadRowHeight.class)); this.contentRowHeightProperty = RowHeightProperty.build((ContentRowHeight)headClazz.getAnnotation(ContentRowHeight.class)); + this.onceAbsoluteMergeProperty = + OnceAbsoluteMergeProperty.build((OnceAbsoluteMerge)headClazz.getAnnotation(OnceAbsoluteMerge.class)); ColumnWidth parentColumnWidth = (ColumnWidth)headClazz.getAnnotation(ColumnWidth.class); + HeadStyle parentHeadStyle = (HeadStyle)headClazz.getAnnotation(HeadStyle.class); + HeadFontStyle parentHeadFontStyle = (HeadFontStyle)headClazz.getAnnotation(HeadFontStyle.class); + ContentStyle parentContentStyle = (ContentStyle)headClazz.getAnnotation(ContentStyle.class); + ContentFontStyle parentContentFontStyle = (ContentFontStyle)headClazz.getAnnotation(ContentFontStyle.class); + for (Map.Entry entry : getContentPropertyMap().entrySet()) { Integer index = entry.getKey(); ExcelContentProperty excelContentPropertyData = entry.getValue(); @@ -54,6 +72,31 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty { } headData.setColumnWidthProperty(ColumnWidthProperty.build(columnWidth)); + HeadStyle headStyle = field.getAnnotation(HeadStyle.class); + if (headStyle == null) { + headStyle = parentHeadStyle; + } + headData.setHeadStyleProperty(StyleProperty.build(headStyle)); + + HeadFontStyle headFontStyle = field.getAnnotation(HeadFontStyle.class); + if (headFontStyle == null) { + headFontStyle = parentHeadFontStyle; + } + headData.setHeadFontProperty(FontProperty.build(headFontStyle)); + + ContentStyle contentStyle = field.getAnnotation(ContentStyle.class); + if (contentStyle == null) { + contentStyle = parentContentStyle; + } + headData.setContentStyleProperty(StyleProperty.build(contentStyle)); + + ContentFontStyle contentFontStyle = field.getAnnotation(ContentFontStyle.class); + if (contentFontStyle == null) { + contentFontStyle = parentContentFontStyle; + } + headData.setContentFontProperty(FontProperty.build(contentFontStyle)); + + headData.setLoopMergeProperty(LoopMergeProperty.build(field.getAnnotation(ContentLoopMerge.class))); // If have @NumberFormat, 'NumberStringConverter' is specified by default if (excelContentPropertyData.getConverter() == null) { NumberFormat numberFormat = field.getAnnotation(NumberFormat.class); @@ -81,6 +124,14 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty { this.contentRowHeightProperty = contentRowHeightProperty; } + public OnceAbsoluteMergeProperty getOnceAbsoluteMergeProperty() { + return onceAbsoluteMergeProperty; + } + + public void setOnceAbsoluteMergeProperty(OnceAbsoluteMergeProperty onceAbsoluteMergeProperty) { + this.onceAbsoluteMergeProperty = onceAbsoluteMergeProperty; + } + /** * Calculate all cells that need to be merged * diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java index 7aafc44..4790ab1 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -40,6 +40,12 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + + } + @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java index 07afcfc..005f624 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -3,12 +3,11 @@ package com.alibaba.excel.write.style.column; import java.util.List; import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Row; import com.alibaba.excel.event.NotRepeatExecutor; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.AbstractCellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; @@ -17,7 +16,7 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; * * @author Jiaju Zhuang */ -public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandler, NotRepeatExecutor { +public abstract class AbstractColumnWidthStyleStrategy extends AbstractCellWriteHandler implements NotRepeatExecutor { @Override public String uniqueValue() { @@ -25,15 +24,8 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl } @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} - - @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, Integer relativeRowIndex, Boolean isHead) {} - - @Override - public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { setColumnWidth(writeSheetHolder, cellDataList, cell, head, relativeRowIndex, isHead); } @@ -47,7 +39,7 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl * @param relativeRowIndex * @param isHead */ - protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, - Integer relativeRowIndex, Boolean isHead); + protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead); } 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 abd5e1c..210ca14 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 @@ -32,7 +32,7 @@ public class ConverterDataListener extends AnalysisEventListener Assert.assertEquals(list.size(), 1); ConverterData data = list.get(0); try { - Assert.assertEquals(data.getDate(), DateUtils.parseDate("2020-01-01 01:01:01")); + Assert.assertEquals(DateUtils.parseDate("2020-01-01 01:01:01"), data.getDate()); } catch (ParseException e) { throw new ExcelCommonException("Test Exception", e); } 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 index c8b5d8b..d5da81a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java @@ -64,7 +64,7 @@ public class ReadAllConverterDataListener extends AnalysisEventListener list = + EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync(); + for (DateFormatData data : list) { + Assert.assertEquals(data.getDateStringCn(), data.getDate()); + Assert.assertEquals(data.getNumberStringCn(), data.getNumber()); + } + } + + private void readUs(File file) { + List list = + EasyExcel.read(file, DateFormatData.class, null).locale(Locale.US).sheet().doReadSync(); + for (DateFormatData data : list) { + Assert.assertEquals(data.getDateStringUs(), data.getDate()); + Assert.assertEquals(data.getNumberStringUs(), data.getNumber()); + } + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java index 98f580f..375f9be 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -6,19 +6,15 @@ import java.io.FileOutputStream; 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; import com.alibaba.easyexcel.test.core.simple.SimpleData; -import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; -import com.alibaba.easyexcel.test.core.simple.SimpleDataSheetNameListener; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.util.FileUtils; /** * @@ -51,12 +47,12 @@ public class EncryptDataTest { } @Test - public void t01ReadAndWriteStream07() throws Exception { + public void t03ReadAndWriteStream07() throws Exception { readAndWriteStream(file07OutputStream, ExcelTypeEnum.XLSX); } @Test - public void t02ReadAndWriteStream03() throws Exception { + public void t04ReadAndWriteStream03() throws Exception { readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLS); } @@ -72,8 +68,8 @@ public class EncryptDataTest { fileOutputStream.close(); FileInputStream fileInputStream = new FileInputStream(file); - EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456") - .excelType(excelType).sheet().doRead(); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456").sheet() + .doRead(); } private List data() { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java new file mode 100644 index 0000000..61822fa --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java @@ -0,0 +1,14 @@ +package com.alibaba.easyexcel.test.core.extra; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class ExtraData { + + private String row1; + + private String row2; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java new file mode 100644 index 0000000..9039ab0 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java @@ -0,0 +1,55 @@ +package com.alibaba.easyexcel.test.core.extra; + +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.excel.metadata.CellExtra; +import com.alibaba.fastjson.JSON; + +/** + * @author Jiaju Zhuang + */ +public class ExtraDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ExtraData.class); + + @Override + public void invoke(ExtraData data, AnalysisContext context) {} + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + LOGGER.info("extra data:{}", JSON.toJSONString(extra)); + switch (extra.getType()) { + case COMMENT: + Assert.assertEquals("批注的内容", extra.getText()); + Assert.assertEquals(4, (int)extra.getRowIndex()); + Assert.assertEquals(0, (int)extra.getColumnIndex()); + break; + case HYPERLINK: + if ("Sheet1!A1".equals(extra.getText())) { + Assert.assertEquals(1, (int)extra.getRowIndex()); + Assert.assertEquals(0, (int)extra.getColumnIndex()); + } else if ("Sheet2!A1".equals(extra.getText())) { + Assert.assertEquals(2, (int)extra.getFirstRowIndex()); + Assert.assertEquals(0, (int)extra.getFirstColumnIndex()); + Assert.assertEquals(3, (int)extra.getLastRowIndex()); + Assert.assertEquals(1, (int)extra.getLastColumnIndex()); + } else { + Assert.fail("Unknown hyperlink!"); + } + break; + case MERGE: + Assert.assertEquals(5, (int)extra.getFirstRowIndex()); + Assert.assertEquals(0, (int)extra.getFirstColumnIndex()); + Assert.assertEquals(6, (int)extra.getLastRowIndex()); + Assert.assertEquals(1, (int)extra.getLastColumnIndex()); + break; + default: + } + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java new file mode 100644 index 0000000..cf7418d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java @@ -0,0 +1,44 @@ +package com.alibaba.easyexcel.test.core.extra; + +import java.io.File; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellExtraTypeEnum; + +/** + * + * @author Jiaju Zhuang + */ +public class ExtraDataTest { + private static final Logger LOGGER = LoggerFactory.getLogger(ExtraDataTest.class); + private static File file03; + private static File file07; + + @BeforeClass + public static void init() { + file03 = TestFileUtil.readFile("extra" + File.separator + "extra.xls"); + file07 = TestFileUtil.readFile("extra" + File.separator + "extra.xlsx"); + } + + @Test + public void t01Read07() { + read(file07); + } + + @Test + public void t02Read03() { + read(file03); + } + + private void read(File file) { + EasyExcel.read(file, ExtraData.class, new ExtraDataListener()).extraRead(CellExtraTypeEnum.COMMENT) + .extraRead(CellExtraTypeEnum.HYPERLINK).extraRead(CellExtraTypeEnum.MERGE).sheet().doRead(); + } + +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java index fe02163..5c40030 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -19,6 +19,7 @@ import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; /** * @@ -43,6 +44,10 @@ public class FillDataTest { private static File byName03; private static File byNameTemplate07; private static File byNameTemplate03; + private static File fileComposite07; + private static File compositeFillTemplate07; + private static File fileComposite03; + private static File compositeFillTemplate03; @BeforeClass public static void init() { @@ -62,6 +67,10 @@ public class FillDataTest { byNameTemplate07 = TestFileUtil.readFile("fill" + File.separator + "byName.xlsx"); byName03 = TestFileUtil.createNewFile("byName03.xls"); byNameTemplate03 = TestFileUtil.readFile("fill" + File.separator + "byName.xls"); + fileComposite07 = TestFileUtil.createNewFile("fileComposite07.xlsx"); + compositeFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "composite.xlsx"); + fileComposite03 = TestFileUtil.createNewFile("fileComposite03.xls"); + compositeFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "composite.xls"); } @Test @@ -104,6 +113,16 @@ public class FillDataTest { byNameFill(byName03, byNameTemplate03); } + @Test + public void t09CompositeFill07() { + compositeFill(fileComposite07, compositeFillTemplate07); + } + + @Test + public void t10CompositeFill03() { + compositeFill(fileComposite03, compositeFillTemplate03); + } + private void byNameFill(File file, File template) { FillData fillData = new FillData(); fillData.setName("张三"); @@ -111,6 +130,31 @@ public class FillDataTest { EasyExcel.write(file, FillData.class).withTemplate(template).sheet("Sheet2").doFill(fillData); } + private void compositeFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + + List list = EasyExcel.read(file).ignoreEmptyRow(false).sheet().headRowNumber(0).doReadSync(); + Map map0 = (Map)list.get(0); + Assert.assertEquals("张三", map0.get(21)); + Map map27 = (Map)list.get(27); + Assert.assertEquals("张三", map27.get(0)); + Map map29 = (Map)list.get(29); + Assert.assertEquals("张三", map29.get(3)); + } + private void horizontalFill(File file, File template) { ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java index 81e85ae..c57976a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java @@ -29,10 +29,10 @@ public class ListHeadDataListener extends AnalysisEventListener data = list.get(0); - Assert.assertEquals(data.get(0), "字符串0"); - Assert.assertEquals(data.get(1), "1.0"); - Assert.assertEquals(data.get(2), "2020-01-01 01:01:01"); - Assert.assertEquals(data.get(3), "额外数据"); + Assert.assertEquals("字符串0", data.get(0)); + Assert.assertEquals("1", data.get(1)); + Assert.assertEquals("2020-01-01 01:01:01", data.get(2)); + Assert.assertEquals("额外数据", data.get(3)); LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java index 32a0185..068045e 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -15,6 +15,7 @@ import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; /** * 写的填充写法 @@ -179,6 +180,38 @@ public class FillTest { excelWriter.finish(); } + /** + * 组合填充填充 + * + * @since 2.2.0 + */ + @Test + public void compositeFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx"; + + String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹 + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java new file mode 100644 index 0000000..e780bfa --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java @@ -0,0 +1,14 @@ +package com.alibaba.easyexcel.test.demo.read; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class DemoExtraData { + + private String row1; + + private String row2; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java new file mode 100644 index 0000000..01adfe7 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java @@ -0,0 +1,58 @@ +package com.alibaba.easyexcel.test.demo.read; + +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.excel.metadata.CellExtra; +import com.alibaba.fastjson.JSON; + +/** + * 读取单元格的批注 + * + * @author: kaiux + * @date: 2019-10-23 14:10 + **/ +public class DemoExtraListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); + + @Override + public void invoke(DemoExtraData data, AnalysisContext context) {} + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + LOGGER.info("读取到了一条额外信息:{}", JSON.toJSONString(extra)); + switch (extra.getType()) { + case COMMENT: + LOGGER.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(), + extra.getText()); + break; + case HYPERLINK: + if ("Sheet1!A1".equals(extra.getText())) { + LOGGER.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), + extra.getColumnIndex(), extra.getText()); + } else if ("Sheet2!A1".equals(extra.getText())) { + LOGGER.info( + "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}," + + "内容是:{}", + extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), + extra.getLastColumnIndex(), extra.getText()); + } else { + Assert.fail("Unknown hyperlink!"); + } + break; + case MERGE: + LOGGER.info( + "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}", + extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), + extra.getLastColumnIndex()); + break; + default: + } + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java index 0dc22aa..5234f05 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -16,6 +16,7 @@ import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; import com.alibaba.excel.converters.DefaultConverterLoader; +import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.fastjson.JSON; @@ -33,7 +34,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

* 3. 直接读即可 */ @@ -60,7 +61,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener} *

* 3. 直接读即可 */ @@ -76,7 +77,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

* 3. 直接读即可 */ @@ -108,7 +109,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener} *

* 3. 直接读即可 */ @@ -130,7 +131,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

* 3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数, * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。 @@ -150,7 +151,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} *

* 3. 直接读即可 */ @@ -161,13 +162,38 @@ public class ReadTest { EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } + /** + * 额外信息(批注、超链接、合并单元格信息读取) + *

+ * 由于是流式读取,没法在读取到单元格数据的时候直接读取到额外信息,所以只能最后通知哪些单元格有哪些额外信息 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoExtraData} + *

+ * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExtraListener} + *

+ * 3. 直接读即可 + */ + @Test + public void extraRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "extra.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, DemoExtraData.class, new DemoExtraListener()) + // 需要读取批注 默认不读取 + .extraRead(CellExtraTypeEnum.COMMENT) + // 需要读取超链接 默认不读取 + .extraRead(CellExtraTypeEnum.HYPERLINK) + // 需要读取合并单元格信息 默认不读取 + .extraRead(CellExtraTypeEnum.MERGE).sheet().doRead(); + } + /** * 读取公式和单元格类型 * *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} *

* 3. 直接读即可 */ @@ -184,7 +210,7 @@ public class ReadTest { *

* 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} *

* 3. 直接读即可 */ @@ -216,7 +242,7 @@ public class ReadTest { } /** - * 不创建对象的读,不是特别推荐使用,都用String接收对日期的支持不是很好 + * 不创建对象的读 */ @Test public void noModleRead() { diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java index 98f400a..b80022d 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java @@ -85,7 +85,7 @@ public class WebTest { *

* 1. 创建excel对应的实体对象 参照{@link UploadData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} + * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} *

* 3. 直接读即可 */ diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java index b92d44a..b1bcb7d 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java @@ -6,13 +6,12 @@ import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Hyperlink; -import org.apache.poi.ss.usermodel.Row; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.AbstractCellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; @@ -21,22 +20,10 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; * * @author Jiaju Zhuang */ -public class CustomCellWriteHandler implements CellWriteHandler { +public class CustomCellWriteHandler extends AbstractCellWriteHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class); - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { - - } - - @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, Integer relativeRowIndex, Boolean isHead) { - - } - @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java new file mode 100644 index 0000000..ad94321 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java @@ -0,0 +1,27 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; + +import lombok.Data; + +/** + * 样式的数据类 + * + * @author Jiaju Zhuang + **/ +@Data +// 将第6-7行的2-3列合并成一个单元格 +// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2) +public class DemoMergeData { + // 这一列 每隔2行 合并单元格 + @ContentLoopMerge(eachRow = 2) + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java new file mode 100644 index 0000000..c73a088 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java @@ -0,0 +1,44 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import org.apache.poi.ss.usermodel.FillPatternType; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; + +import lombok.Data; + +/** + * 样式的数据类 + * + * @author Jiaju Zhuang + **/ +@Data +// 头背景设置成红色 IndexedColors.RED.getIndex() +@HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 10) +// 头字体设置成20 +@HeadFontStyle(fontHeightInPoints = 20) +// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex() +@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 17) +// 内容字体设置成20 +@ContentFontStyle(fontHeightInPoints = 20) +public class DemoStyleData { + // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex() + @HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 14) + // 字符串的头字体设置成20 + @HeadFontStyle(fontHeightInPoints = 30) + // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex() + @ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 40) + // 字符串的内容字体设置成20 + @ContentFontStyle(fontHeightInPoints = 30) + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 7b7d6b3..8b5fe53 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -270,7 +270,21 @@ public class WriteTest { } /** - * 自定义样式 + * 注解形式自定义样式 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoStyleData} + *

+ * 3. 直接写即可 + */ + @Test + public void annotationStyleWrite() { + String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoStyleData.class).sheet("模板").doWrite(data()); + } + + /** + * 拦截器形式自定义样式 *

* 1. 创建excel对应的实体对象 参照{@link DemoData} *

@@ -279,8 +293,8 @@ public class WriteTest { * 3. 直接写即可 */ @Test - public void styleWrite() { - String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx"; + public void handlerStyleWrite() { + String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 背景设置为红色 @@ -310,7 +324,7 @@ public class WriteTest { /** * 合并单元格 *

- * 1. 创建excel对应的实体对象 参照{@link DemoData} + * 1. 创建excel对应的实体对象 参照{@link DemoData} {@link DemoMergeData} *

* 2. 创建一个merge策略 并注册 *

@@ -318,7 +332,14 @@ public class WriteTest { */ @Test public void mergeWrite() { + // 方法1 注解 String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; + // 在DemoStyleData里面加上ContentLoopMerge注解 + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoMergeData.class).sheet("模板").doWrite(data()); + + // 方法2 自定义合并单元格策略 + fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写 LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 @@ -414,6 +435,23 @@ public class WriteTest { .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); } + /** + * 可变标题处理(包括标题国际化等) + *

+ * 简单的说用List>的标题 但是还支持注解 + *

+ * 1. 创建excel对应的实体对象 参照{@link ConverterData} + *

+ * 2. 直接写即可 + */ + @Test + public void variableTitleWrite() { + // 写法1 + String fileName = TestFileUtil.getPath() + "variableTitleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, ConverterData.class).head(variableTitleHead()).sheet("模板").doWrite(data()); + } + /** * 不创建对象的写 */ @@ -437,6 +475,20 @@ public class WriteTest { return list; } + private List> variableTitleHead() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("string" + System.currentTimeMillis()); + List head1 = new ArrayList(); + head1.add("number" + System.currentTimeMillis()); + List head2 = new ArrayList(); + head2.add("date" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + private List> head() { List> list = new ArrayList>(); List head0 = new ArrayList(); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java index 8049d89..0ed625d 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java @@ -14,7 +14,6 @@ import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; -import com.alibaba.excel.write.metadata.fill.FillConfig; /** * 写的填充写法 @@ -34,18 +33,12 @@ public class FillTempTest { public void complexFill() { // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = "D:\\test\\complex.xlsx"; + String templateFileName = "D:\\test\\simple.xlsx"; String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 - // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 - // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 - // 如果数据量大 list不是最后一行 参照下一个 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - excelWriter.fill(data(), fillConfig, writeSheet); -// excelWriter.fill(data2(), fillConfig, writeSheet); + excelWriter.fill(teamp(), writeSheet); Map map = new HashMap(); map.put("date", "2019年10月9日13:28:28"); map.put("total", 1000); @@ -106,6 +99,17 @@ public class FillTempTest { return list; } + private List teamp() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + TempFillData fillData = new TempFillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java index f72e94d..6c9bd8d 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -1,14 +1,24 @@ package com.alibaba.easyexcel.test.temp; import java.io.File; +import java.util.ArrayList; +import java.util.Date; import java.util.List; +import org.apache.poi.hssf.util.CellReference; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.fastjson.JSON; /** @@ -22,7 +32,7 @@ public class Lock2Test { @Test public void test() throws Exception { - File file = new File("D:\\test\\000001.xlsx"); + File file = new File("D:\\test\\headt1.xlsx"); List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); LOGGER.info("数据:{}", list.size()); @@ -31,6 +41,52 @@ public class Lock2Test { } } + @Test + public void write() throws Exception { + String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx"; + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 背景设置为红色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteCellStyle.setWriteFont(headWriteFont); + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + // 字体大小 + contentWriteFont.setFontHeightInPoints((short)20); + contentWriteCellStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板") + .doWrite(data()); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + + @Test + public void testc() throws Exception { + LOGGER.info("reslut:{}", JSON.toJSONString(new CellReference("B3"))); + } + @Test public void simpleRead() { // 写法1: diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java b/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java new file mode 100644 index 0000000..55984a7 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.temp; + +import com.alibaba.excel.annotation.write.style.ContentRowHeight; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +@ContentRowHeight(30) +public class TempFillData { + private String name; + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java new file mode 100644 index 0000000..d057d8e --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java @@ -0,0 +1,16 @@ +package com.alibaba.easyexcel.test.temp.dataformat; + +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * TODO + * + * @author 罗成 + **/ +@Data +public class DataFormatData { + private CellData date; + private CellData num; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java new file mode 100644 index 0000000..e165d98 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java @@ -0,0 +1,190 @@ +package com.alibaba.easyexcel.test.temp.dataformat; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.easyexcel.test.core.dataformat.DateFormatData; +import com.alibaba.easyexcel.test.temp.Lock2Test; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson.JSON; + +/** + * 格式测试 + * + * @author Jiaju Zhuang + **/ +@Ignore +public class DataFormatTest { + private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class); + + @Test + public void test() throws Exception { + File file = new File("D:\\test\\dataformat.xlsx"); + + List list = + EasyExcel.read(file, DataFormatData.class, null).sheet().headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (DataFormatData data : list) { + Integer dataFormat = data.getDate().getDataFormat(); + + String dataFormatString = data.getDate().getDataFormatString(); + + if (dataFormat == null || dataFormatString == null) { + + } else { + LOGGER.info("格式化:{};{}:{}", dataFormat, dataFormatString, + DateUtil.isADateFormat(dataFormat, dataFormatString)); + } + + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void testxls() throws Exception { + File file = new File("D:\\test\\dataformat.xls"); + + List list = + EasyExcel.read(file, DataFormatData.class, null).sheet().headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (DataFormatData data : list) { + Integer dataFormat = data.getDate().getDataFormat(); + + String dataFormatString = data.getDate().getDataFormatString(); + + if (dataFormat == null || dataFormatString == null) { + + } else { + LOGGER.info("格式化:{};{}:{}", dataFormat, dataFormatString, + DateUtil.isADateFormat(dataFormat, dataFormatString)); + } + + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void test3() throws IOException { + String file = "D:\\test\\dataformat1.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).getCell(0); + DataFormatter d = new DataFormatter(); + System.out.println(d.formatCellValue(cell)); + } + + @Test + public void test31() throws IOException { + System.out.println(DateUtil.isADateFormat(181, "[DBNum1][$-404]m\"\u6708\"d\"\u65e5\";@")); + } + + @Test + public void test43() throws IOException { + SimpleDateFormat s = new SimpleDateFormat("yyyy'年'm'月'd'日' h'点'mm'哈哈哈m'"); + System.out.println(s.format(new Date())); + } + + @Test + public void test463() throws IOException { + SimpleDateFormat s = new SimpleDateFormat("[$-804]yyyy年m月"); + System.out.println(s.format(new Date())); + } + + @Test + public void test1() throws Exception { + System.out.println(DateUtil.isADateFormat(181, "yyyy\"年啊\"m\"月\"d\"日\"\\ h")); + System.out.println(DateUtil.isADateFormat(180, "yyyy\"年\"m\"月\"d\"日\"\\ h\"点\"")); + } + + @Test + public void test2() throws Exception { + List list1 = new ArrayList(3000); + long start = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + list1.clear(); + } + System.out.println("end:" + (System.currentTimeMillis() - start)); + start = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + list1 = new ArrayList(3000); + } + System.out.println("end:" + (System.currentTimeMillis() - start)); + } + + @Test + public void test355() throws IOException, InvalidFormatException { + File file = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xlsx"); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + DataFormatter d = new DataFormatter(Locale.CHINA); + + for (int i = 0; i < xssfSheet.getLastRowNum(); i++) { + Row row = xssfSheet.getRow(i); + System.out.println(d.formatCellValue(row.getCell(0))); + } + + } + + @Test + public void test3556() throws IOException, InvalidFormatException { + String file = "D://test/dataformat1.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + DataFormatter d = new DataFormatter(Locale.CHINA); + + for (int i = 0; i < xssfSheet.getLastRowNum(); i++) { + Row row = xssfSheet.getRow(i); + System.out.println(d.formatCellValue(row.getCell(0))); + } + + } + + @Test + public void tests() throws IOException, InvalidFormatException { + SimpleDateFormat s1 = new SimpleDateFormat("yyyy\"5E74\"m\"6708\"d\"65E5\""); + System.out.println(s1.format(new Date())); + s1 = new SimpleDateFormat("yyyy年m月d日"); + System.out.println(s1.format(new Date())); + } + + @Test + public void tests1() throws IOException, InvalidFormatException { + String file = "D://test/dataformat1.xlsx"; + List list = EasyExcel.read(file, DateFormatData.class, null).sheet().doReadSync(); + for (DateFormatData data : list) { + LOGGER.info("返回:{}", JSON.toJSONString(data)); + } + } + + @Test + public void tests3() throws IOException, InvalidFormatException { + SimpleDateFormat s1 = new SimpleDateFormat("ah\"时\"mm\"分\""); + System.out.println(s1.format(new Date())); + } + + private static final Pattern date_ptrn6 = Pattern.compile("^.*(年|月|日|时|分|秒)+.*$"); + + @Test + public void tests34() throws IOException, InvalidFormatException { + System.out.println(date_ptrn6.matcher("2017但是").matches()); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java new file mode 100644 index 0000000..0239ed5 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java @@ -0,0 +1,1292 @@ +/* + * ==================================================================== Licensed to the Apache Software Foundation (ASF) + * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff + * can be found in svn at location root/projects/3rd-party/src + * ==================================================================== + */ +package com.alibaba.easyexcel.test.temp.dataformat; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.poi.ss.format.CellFormat; +import org.apache.poi.ss.format.CellFormatResult; +import org.apache.poi.ss.formula.ConditionalFormattingEvaluator; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat; +import org.apache.poi.ss.usermodel.ExcelNumberFormat; +import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; +import org.apache.poi.ss.usermodel.FormulaError; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.FractionFormat; +import org.apache.poi.ss.util.DateFormatConverter; +import org.apache.poi.ss.util.NumberToTextConverter; +import org.apache.poi.util.LocaleUtil; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * DataFormatter contains methods for formatting the value stored in an Cell. This can be useful for reports and GUI + * presentations when you need to display data exactly as it appears in Excel. Supported formats include currency, SSN, + * percentages, decimals, dates, phone numbers, zip codes, etc. + *

+ * Internally, formats will be implemented using subclasses of {@link Format} such as {@link DecimalFormat} and + * {@link java.text.SimpleDateFormat}. Therefore the formats used by this class must obey the same pattern rules as + * these Format subclasses. This means that only legal number pattern characters ("0", "#", ".", "," etc.) may appear in + * number formats. Other characters can be inserted before or after the number pattern to form a + * prefix or suffix. + *

+ *

+ * For example the Excel pattern "$#,##0.00 "USD"_);($#,##0.00 "USD")" + * will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)". However the pattern + * "00-00-00" is incorrectly formatted by DecimalFormat as "000000--". For Excel formats that are not + * compatible with DecimalFormat, you can provide your own custom {@link Format} implementation via + * DataFormatter.addFormat(String,Format). The following custom formats are already provided by this class: + *

+ * + *
+ * 
  • SSN "000-00-0000"
  • + *
  • Phone Number "(###) ###-####"
  • + *
  • Zip plus 4 "00000-0000"
  • + *
+ *
+ *

+ * If the Excel format pattern cannot be parsed successfully, then a default format will be used. The default number + * format will mimic the Excel General format: "#" for whole numbers and "#.##########" for decimal numbers. You can + * override the default format pattern with + * DataFormatter.setDefaultNumberFormat(Format). Note: the default format will only be used when a Format + * cannot be created from the cell's data format string. + * + *

+ * Note that by default formatted numeric values are trimmed. Excel formats can contain spacers and padding and the + * default behavior is to strip them off. + *

+ *

+ * Example: + *

+ *

+ * Consider a numeric cell with a value 12.343 and format "##.##_ ". The trailing underscore + * and space ("_ ") in the format adds a space to the end and Excel formats this cell as "12.34 ", but + * DataFormatter trims the formatted value and returns "12.34". + *

+ * You can enable spaces by passing the emulateCSV=true flag in the DateFormatter cosntructor. + * If set to true, then the output tries to conform to what you get when you take an xls or xlsx in Excel and Save As + * CSV file: + *
    + *
  • returned values are not trimmed
  • + *
  • Invalid dates are formatted as 255 pound signs ("#")
  • + *
  • simulate Excel's handling of a format string of all # when the value is 0. Excel will output "", + * DataFormatter will output "0". + *
+ *

+ * Some formats are automatically "localized" by Excel, eg show as mm/dd/yyyy when loaded in Excel in some Locales but + * as dd/mm/yyyy in others. These are always returned in the "default" (US) format, as stored in the file. Some format + * strings request an alternate locale, eg [$-809]d/m/yy h:mm AM/PM which explicitly requests UK locale. + * These locale directives are (currently) ignored. You can use {@link DateFormatConverter} to do some of this + * localisation if you need it. + */ +public class DataFormatter1 implements Observer { + private static final String defaultFractionWholePartFormat = "#"; + private static final String defaultFractionFractionPartFormat = "#/##"; + /** 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...." */ + private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE); + + /** Pattern to find "AM/PM" marker */ + private static final Pattern amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE); + + /** Pattern to find formats with condition ranges e.g. [>=100] */ + private static final Pattern rangeConditionalPattern = + Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); + + /** + * A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into + * locales + */ + private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])"); + + /** + * A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red, + * White, Yellow, "Color n" (1<=n<=56) + */ + private static final Pattern colorPattern = Pattern.compile("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" + + "(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" + + "(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", Pattern.CASE_INSENSITIVE); + + /** + * A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called + */ + private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*\\/\\s*([#\\d]+)"); + + /** + * A regex to strip junk out of fraction formats + */ + private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ \\?#\\d\\/]+)"); + + /** + * A regex to detect if an alternate grouping character is used in a numeric format + */ + private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})"); + + /** + * Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs + * ("#"). + */ + private static final String invalidDateTimeString; + static { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 255; i++) + buf.append('#'); + invalidDateTimeString = buf.toString(); + } + + /** + * The decimal symbols of the locale used for formatting values. + */ + private DecimalFormatSymbols decimalSymbols; + + /** + * The date symbols of the locale used for formatting values. + */ + private DateFormatSymbols dateSymbols; + + /** + * A default date format, if no date format was given + */ + private DateFormat defaultDateformat; + + /** General format for numbers. */ + private Format generalNumberFormat; + + /** A default format to use when a number pattern cannot be parsed. */ + private Format defaultNumFormat; + + /** + * A map to cache formats. Map formats + */ + private final Map formats = new HashMap(); + + private final boolean emulateCSV; + + /** stores the locale valid it the last formatting call */ + private Locale locale; + + /** stores if the locale should change according to {@link LocaleUtil#getUserLocale()} */ + private boolean localeIsAdapting; + + private class LocaleChangeObservable extends Observable { + void checkForLocaleChange() { + checkForLocaleChange(LocaleUtil.getUserLocale()); + } + + void checkForLocaleChange(Locale newLocale) { + if (!localeIsAdapting) + return; + if (newLocale.equals(locale)) + return; + super.setChanged(); + notifyObservers(newLocale); + } + } + + /** the Observable to notify, when the locale has been changed */ + private final LocaleChangeObservable localeChangedObservable = new LocaleChangeObservable(); + + /** For logging any problems we find */ + private static POILogger logger = POILogFactory.getLogger(DataFormatter.class); + + /** + * Creates a formatter using the {@link Locale#getDefault() default locale}. + */ + public DataFormatter1() { + this(false); + } + + /** + * Creates a formatter using the {@link Locale#getDefault() default locale}. + * + * @param emulateCSV + * whether to emulate CSV output. + */ + public DataFormatter1(boolean emulateCSV) { + this(LocaleUtil.getUserLocale(), true, emulateCSV); + } + + /** + * Creates a formatter using the given locale. + */ + public DataFormatter1(Locale locale) { + this(locale, false); + } + + /** + * Creates a formatter using the given locale. + * + * @param emulateCSV + * whether to emulate CSV output. + */ + public DataFormatter1(Locale locale, boolean emulateCSV) { + this(locale, false, emulateCSV); + } + + /** + * Creates a formatter using the given locale. + * + * @param localeIsAdapting + * (true only if locale is not user-specified) + * @param emulateCSV + * whether to emulate CSV output. + */ + private DataFormatter1(Locale locale, boolean localeIsAdapting, boolean emulateCSV) { + this.localeIsAdapting = true; + localeChangedObservable.addObserver(this); + // localeIsAdapting must be true prior to this first checkForLocaleChange call. + localeChangedObservable.checkForLocaleChange(locale); + // set localeIsAdapting so subsequent checks perform correctly + // (whether a specific locale was provided to this DataFormatter or DataFormatter should + // adapt to the current user locale as the locale changes) + this.localeIsAdapting = localeIsAdapting; + this.emulateCSV = emulateCSV; + } + + /** + * Return a Format for the given cell if one exists, otherwise try to create one. This method will return + * null if the any of the following is true: + *

    + *
  • the cell's style is null
  • + *
  • the style's data format string is null or empty
  • + *
  • the format string cannot be recognized as either a number or date
  • + *
+ * + * @param cell + * The cell to retrieve a Format for + * @return A Format for the format String + */ + private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + if (cell == null) + return null; + + ExcelNumberFormat numFmt = ExcelNumberFormat.from(cell, cfEvaluator); + + if (numFmt == null) { + return null; + } + + int formatIndex = numFmt.getIdx(); + String formatStr = numFmt.getFormat(); + if (formatStr == null || formatStr.trim().length() == 0) { + return null; + } + return getFormat(cell.getNumericCellValue(), formatIndex, formatStr); + } + + private Format getFormat(double cellValue, int formatIndex, String formatStrIn) { + localeChangedObservable.checkForLocaleChange(); + + // 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. + // String[] formatBits = formatStrIn.split(";"); + // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; + // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; + + String formatStr = formatStrIn; + + // Excel supports 2+ part conditional data formats, eg positive/negative/zero, + // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds + // of different formats for different ranges, just +ve/-ve, we need to + // handle these ourselves in a special way. + // For now, if we detect 2+ parts, we call out to CellFormat to handle it + // TODO Going forward, we should really merge the logic between the two classes + 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); + // CellFormat requires callers to identify date vs not, so do so + Object cellValueO = Double.valueOf(cellValue); + if (DateUtil.isADateFormat(formatIndex, formatStr) && + // don't try to handle Date value 0, let a 3 or 4-part format take care of it + ((Double)cellValueO).doubleValue() != 0.0) { + cellValueO = DateUtil.getJavaDate(cellValue); + } + // Wrap and return (non-cachable - CellFormat does that) + return new CellFormatResultWrapper(cfmt.apply(cellValueO)); + } catch (Exception e) { + logger.log(POILogger.WARN, "Formatting failed for format " + formatStr + ", falling back", e); + } + } + + // Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format. + if (emulateCSV && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) { + formatStr = formatStr.replaceAll("#", ""); + } + + // See if we already have it cached + Format format = formats.get(formatStr); + if (format != null) { + return format; + } + + // Is it one of the special built in types, General or @? + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return generalNumberFormat; + } + + // Build a formatter, and cache it + format = createFormat(cellValue, formatIndex, formatStr); + formats.put(formatStr, format); + return format; + } + + /** + * Create and return a Format based on the format string from a cell's style. If the pattern cannot be parsed, + * return a default pattern. + * + * @param cell + * The Excel cell + * @return A Format representing the excel format. May return null. + */ + public Format createFormat(Cell cell) { + + int formatIndex = cell.getCellStyle().getDataFormat(); + String formatStr = cell.getCellStyle().getDataFormatString(); + return createFormat(cell.getNumericCellValue(), formatIndex, formatStr); + } + + private Format createFormat(double cellValue, int formatIndex, String sFormat) { + localeChangedObservable.checkForLocaleChange(); + + String formatStr = sFormat; + + // Remove colour formatting if present + Matcher colourM = colorPattern.matcher(formatStr); + while (colourM.find()) { + String colour = colourM.group(); + + // Paranoid replacement... + int at = formatStr.indexOf(colour); + if (at == -1) + break; + String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); + if (nFormatStr.equals(formatStr)) + break; + + // Try again in case there's multiple + formatStr = nFormatStr; + colourM = colorPattern.matcher(formatStr); + } + + // Strip off the locale information, we use an instance-wide locale for everything + Matcher m = localePatternGroup.matcher(formatStr); + while (m.find()) { + String match = m.group(); + String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-')); + if (symbol.indexOf('$') > -1) { + symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + + symbol.substring(symbol.indexOf('$'), symbol.length()); + } + formatStr = m.replaceAll(symbol); + m = localePatternGroup.matcher(formatStr); + } + + // Check for special cases + if (formatStr == null || formatStr.trim().length() == 0) { + return getDefaultFormat(cellValue); + } + + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return generalNumberFormat; + } + + if ("".equals("")||(DateUtil.isADateFormat(formatIndex, formatStr) && DateUtil.isValidExcelDate(cellValue))) { + return createDateFormat(formatStr, cellValue); + } + // Excel supports fractions in format strings, which Java doesn't + if (formatStr.contains("#/") || formatStr.contains("?/")) { + String[] chunks = formatStr.split(";"); + for (String chunk1 : chunks) { + String chunk = chunk1.replaceAll("\\?", "#"); + Matcher matcher = fractionStripper.matcher(chunk); + chunk = matcher.replaceAll(" "); + chunk = chunk.replaceAll(" +", " "); + Matcher fractionMatcher = fractionPattern.matcher(chunk); + // take the first match + if (fractionMatcher.find()) { + String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat; + return new FractionFormat(wholePart, fractionMatcher.group(3)); + } + } + + // Strip custom text in quotes and escaped characters for now as it can cause performance problems in + // fractions. + // String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", + // "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#"); + // System.out.println("formatStr: "+strippedFormatStr); + return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat); + } + + if (numPattern.matcher(formatStr).find()) { + return createNumberFormat(formatStr, cellValue); + } + + if (emulateCSV) { + return new ConstantStringFormat(cleanFormatForNumber(formatStr)); + } + // TODO - when does this occur? + return null; + } + + private Format createDateFormat(String pFormatStr, double cellValue) { + String formatStr = pFormatStr; + formatStr = formatStr.replaceAll("\\\\-", "-"); + formatStr = formatStr.replaceAll("\\\\,", ","); + formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char + formatStr = formatStr.replaceAll("\\\\ ", " "); + formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy + formatStr = formatStr.replaceAll(";@", ""); + formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy + formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting + formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates + + boolean hasAmPm = false; + Matcher amPmMatcher = amPmPattern.matcher(formatStr); + while (amPmMatcher.find()) { + formatStr = amPmMatcher.replaceAll("@"); + hasAmPm = true; + amPmMatcher = amPmPattern.matcher(formatStr); + } + formatStr = formatStr.replaceAll("@", "a"); + + Matcher dateMatcher = daysAsText.matcher(formatStr); + if (dateMatcher.find()) { + String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E"); + formatStr = dateMatcher.replaceAll(match); + } + + // Convert excel date format to SimpleDateFormat. + // Excel uses lower and upper case 'm' for both minutes and months. + // From Excel help: + /* + The "m" or "mm" code must appear immediately after the "h" or"hh" + code or immediately before the "ss" code; otherwise, Microsoft + Excel displays the month instead of minutes." + */ + + StringBuilder sb = new StringBuilder(); + char[] chars = formatStr.toCharArray(); + boolean mIsMonth = true; + List ms = new ArrayList(); + boolean isElapsed = false; + for (int j = 0; j < chars.length; j++) { + char c = chars[j]; + if (c == '\'') { + sb.append(c); + j++; + + // skip until the next quote + while (j < chars.length) { + c = chars[j]; + sb.append(c); + if (c == '\'') { + break; + } + j++; + } + } else if (c == '[' && !isElapsed) { + isElapsed = true; + mIsMonth = false; + sb.append(c); + } else if (c == ']' && isElapsed) { + isElapsed = false; + sb.append(c); + } else if (isElapsed) { + if (c == 'h' || c == 'H') { + sb.append('H'); + } else if (c == 'm' || c == 'M') { + sb.append('m'); + } else if (c == 's' || c == 'S') { + sb.append('s'); + } else { + sb.append(c); + } + } else if (c == 'h' || c == 'H') { + mIsMonth = false; + if (hasAmPm) { + sb.append('h'); + } else { + sb.append('H'); + } + } else if (c == 'm' || c == 'M') { + if (mIsMonth) { + sb.append('M'); + ms.add(Integer.valueOf(sb.length() - 1)); + } else { + sb.append('m'); + } + } else if (c == 's' || c == 'S') { + sb.append('s'); + // if 'M' precedes 's' it should be minutes ('m') + for (int index : ms) { + if (sb.charAt(index) == 'M') { + sb.replace(index, index + 1, "m"); + } + } + mIsMonth = true; + ms.clear(); + } else if (Character.isLetter(c)) { + mIsMonth = true; + ms.clear(); + if (c == 'y' || c == 'Y') { + sb.append('y'); + } else if (c == 'd' || c == 'D') { + sb.append('d'); + } else { + sb.append(c); + } + } else { + if (Character.isWhitespace(c)) { + ms.clear(); + } + sb.append(c); + } + } + formatStr = sb.toString(); + + try { + return new ExcelStyleDateFormatter(formatStr, dateSymbols); + } catch (IllegalArgumentException iae) { + logger.log(POILogger.DEBUG, "Formatting failed for format " + formatStr + ", falling back", iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(cellValue); + } + + } + + private String cleanFormatForNumber(String formatStr) { + StringBuilder sb = new StringBuilder(formatStr); + + if (emulateCSV) { + // Requested spacers with "_" are replaced by a single space. + // Full-column-width padding "*" are removed. + // Not processing fractions at this time. Replace ? with space. + // This matches CSV output. + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*' || c == '?') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (c == '?') { + sb.setCharAt(i, ' '); + } else if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + if (c == '_') { + sb.setCharAt(i + 1, ' '); + } else { + sb.deleteCharAt(i + 1); + } + // Remove the character too + sb.deleteCharAt(i); + i--; + } + } + } + } else { + // If they requested spacers, with "_", + // remove those as we don't do spacing + // If they requested full-column-width + // padding, with "*", remove those too + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + sb.deleteCharAt(i + 1); + } + // Remove the _ too + sb.deleteCharAt(i); + i--; + } + } + } + + // Now, handle the other aspects like + // quoting and scientific notation + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + // remove quotes and back slashes + if (c == '\\' || c == '"') { + sb.deleteCharAt(i); + i--; + + // for scientific/engineering notation + } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') { + sb.deleteCharAt(i); + i--; + } + } + + return sb.toString(); + } + + private static class InternalDecimalFormatWithScale extends Format { + + private static final Pattern endsWithCommas = Pattern.compile("(,+)$"); + private BigDecimal divider; + private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000); + private final DecimalFormat df; + + private static final String trimTrailingCommas(String s) { + return s.replaceAll(",+$", ""); + } + + public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) { + df = new DecimalFormat(trimTrailingCommas(pattern), symbols); + setExcelStyleRoundingMode(df); + Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern); + if (endsWithCommasMatcher.find()) { + String commas = (endsWithCommasMatcher.group(1)); + BigDecimal temp = BigDecimal.ONE; + for (int i = 0; i < commas.length(); ++i) { + temp = temp.multiply(ONE_THOUSAND); + } + divider = temp; + } else { + divider = null; + } + } + + private Object scaleInput(Object obj) { + if (divider != null) { + if (obj instanceof BigDecimal) { + obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP); + } else if (obj instanceof Double) { + obj = (Double)obj / divider.doubleValue(); + } else { + throw new UnsupportedOperationException(); + } + } + return obj; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + obj = scaleInput(obj); + return df.format(obj, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + } + + private Format createNumberFormat(String formatStr, double cellValue) { + String format = cleanFormatForNumber(formatStr); + DecimalFormatSymbols symbols = decimalSymbols; + + // Do we need to change the grouping character? + // eg for a format like #'##0 which wants 12'345 not 12,345 + Matcher agm = alternateGrouping.matcher(format); + if (agm.find()) { + char grouping = agm.group(2).charAt(0); + // Only replace the grouping character if it is not the default + // grouping character for the US locale (',') in order to enable + // correct grouping for non-US locales. + if (grouping != ',') { + symbols = DecimalFormatSymbols.getInstance(locale); + + symbols.setGroupingSeparator(grouping); + String oldPart = agm.group(1); + String newPart = oldPart.replace(grouping, ','); + format = format.replace(oldPart, newPart); + } + } + + try { + return new InternalDecimalFormatWithScale(format, symbols); + } catch (IllegalArgumentException iae) { + logger.log(POILogger.DEBUG, "Formatting failed for format " + formatStr + ", falling back", iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(cellValue); + } + } + + /** + * Returns a default format for a cell. + * + * @param cell + * The cell + * @return a default format + */ + public Format getDefaultFormat(Cell cell) { + return getDefaultFormat(cell.getNumericCellValue()); + } + + private Format getDefaultFormat(double cellValue) { + localeChangedObservable.checkForLocaleChange(); + + // for numeric cells try user supplied default + if (defaultNumFormat != null) { + return defaultNumFormat; + + // otherwise use general format + } + return generalNumberFormat; + } + + /** + * Performs Excel-style date formatting, using the supplied Date and format + */ + private String performDateFormatting(Date d, Format dateFormat) { + return (dateFormat != null ? dateFormat : defaultDateformat).format(d); + } + + /** + * Returns the formatted value of an Excel date as a String based on the cell's DataFormat. + * i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc. + *

+ * If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a + * 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 cell + * to format + * @param cfEvaluator + * ConditionalFormattingEvaluator (if available) + * @return Formatted value + */ + private String getFormattedDateString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + Format dateFormat = getFormat(cell, cfEvaluator); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(cell.getNumericCellValue()); + } + Date d = cell.getDateCellValue(); + return performDateFormatting(d, dateFormat); + } + + /** + * Returns the formatted value of an Excel number as a String based on the cell's DataFormat. + * Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800) + * 555-1234". + *

+ * Format comes from either the highest priority conditional format rule with a specified format, or from the cell + * style. + * + * @param cell + * The cell + * @param cfEvaluator + * if available, or null + * @return a formatted number string + */ + private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + + Format numberFormat = getFormat(cell, cfEvaluator); + double d = cell.getNumericCellValue(); + if (numberFormat == null) { + return String.valueOf(d); + } + String formatted = numberFormat.format(new Double(d)); + return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation + } + + /** + * Formats the given raw cell value, based on the supplied format index and string, according to excel style rules. + * + * @see #formatCellValue(Cell) + */ + public String formatRawCellContents(double value, int formatIndex, String formatString) { + return formatRawCellContents(value, formatIndex, formatString, false); + } + + /** + * Formats the given raw cell value, based on the supplied format index and string, according to excel style rules. + * + * @see #formatCellValue(Cell) + */ + public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) { + localeChangedObservable.checkForLocaleChange(); + + // Is it a date? + if (DateUtil.isADateFormat(formatIndex, formatString)) { + if (DateUtil.isValidExcelDate(value)) { + Format dateFormat = getFormat(value, formatIndex, formatString); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value); + } + Date d = DateUtil.getJavaDate(value, use1904Windowing); + return performDateFormatting(d, dateFormat); + } + // RK: Invalid dates are 255 #s. + if (emulateCSV) { + return invalidDateTimeString; + } + } + + // else Number + Format numberFormat = getFormat(value, formatIndex, formatString); + if (numberFormat == null) { + return String.valueOf(value); + } + + // When formatting 'value', double to text to BigDecimal produces more + // accurate results than double to Double in JDK8 (as compared to + // previous versions). However, if the value contains E notation, this + // would expand the values, which we do not want, so revert to + // original method. + String result; + final String textValue = NumberToTextConverter.toText(value); + if (textValue.indexOf('E') > -1) { + result = numberFormat.format(new Double(value)); + } else { + result = numberFormat.format(new BigDecimal(textValue)); + } + // Complete scientific notation by adding the missing +. + if (result.indexOf('E') > -1 && !result.contains("E-")) { + result = result.replaceFirst("E", "E+"); + } + return result; + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel format + * pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formulas in formula type cells + * will not be evaluated. + *

+ * + * @param cell + * The cell + * @return the formatted cell value as a String + */ + public String formatCellValue(Cell cell) { + return formatCellValue(cell, null); + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel number + * format pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formula cells will be evaluated + * using the given {@link FormulaEvaluator} if the evaluator is non-null. If the evaluator is null, then the formula + * String will be returned. The caller is responsible for setting the currentRow on the evaluator + *

+ * + * @param cell + * The cell (can be null) + * @param evaluator + * The FormulaEvaluator (can be null) + * @return a string value of the cell + */ + public String formatCellValue(Cell cell, FormulaEvaluator evaluator) { + return formatCellValue(cell, evaluator, null); + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel number + * format pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formula cells will be evaluated + * using the given {@link FormulaEvaluator} if the evaluator is non-null. If the evaluator is null, then the formula + * String will be returned. The caller is responsible for setting the currentRow on the evaluator + *

+ *

+ * When a ConditionalFormattingEvaluator is present, it is checked first to see if there is a number format to + * apply. If multiple rules apply, the last one is used. If no ConditionalFormattingEvaluator is present, no rules + * apply, or the applied rules do not define a format, the cell's style format is used. + *

+ *

+ * The two evaluators should be from the same context, to avoid inconsistencies in cached values. + *

+ * + * @param cell + * The cell (can be null) + * @param evaluator + * The FormulaEvaluator (can be null) + * @param cfEvaluator + * ConditionalFormattingEvaluator (can be null) + * @return a string value of the cell + */ + public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) { + localeChangedObservable.checkForLocaleChange(); + + if (cell == null) { + return ""; + } + + CellType cellType = cell.getCellTypeEnum(); + if (cellType == CellType.FORMULA) { + if (evaluator == null) { + return cell.getCellFormula(); + } + cellType = evaluator.evaluateFormulaCellEnum(cell); + } + switch (cellType) { + case NUMERIC: + +// if (DateUtil.isCellDateFormatted(cell, cfEvaluator)) { + return getFormattedDateString(cell, cfEvaluator); +// } +// return getFormattedNumberString(cell, cfEvaluator); + + case STRING: + return cell.getRichStringCellValue().getString(); + + case BOOLEAN: + return cell.getBooleanCellValue() ? "TRUE" : "FALSE"; + case BLANK: + return ""; + case ERROR: + return FormulaError.forInt(cell.getErrorCellValue()).getString(); + default: + throw new RuntimeException("Unexpected celltype (" + cellType + ")"); + } + } + + /** + *

+ * Sets a default number format to be used when the Excel format cannot be parsed successfully. Note: This is + * a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells + * with the General format. + *

+ *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param format + * A Format instance to be used as a default + * @see java.text.Format#format + */ + public void setDefaultNumberFormat(Format format) { + for (Map.Entry entry : formats.entrySet()) { + if (entry.getValue() == generalNumberFormat) { + entry.setValue(format); + } + } + defaultNumFormat = format; + } + + /** + * Adds a new format to the available formats. + *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param excelFormatStr + * The data format string + * @param format + * A Format instance + */ + public void addFormat(String excelFormatStr, Format format) { + formats.put(excelFormatStr, format); + } + + // Some custom formats + + /** + * @return a DecimalFormat with parseIntegerOnly set true + */ + private static DecimalFormat createIntegerOnlyFormat(String fmt) { + DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT); + DecimalFormat result = new DecimalFormat(fmt, dsf); + result.setParseIntegerOnly(true); + return result; + } + + /** + * Enables excel style rounding mode (round half up) on the Decimal Format given. + */ + public static void setExcelStyleRoundingMode(DecimalFormat format) { + setExcelStyleRoundingMode(format, RoundingMode.HALF_UP); + } + + /** + * Enables custom rounding mode on the given Decimal Format. + * + * @param format + * DecimalFormat + * @param roundingMode + * RoundingMode + */ + public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { + format.setRoundingMode(roundingMode); + } + + /** + * If the Locale has been changed via {@link LocaleUtil#setUserLocale(Locale)} the stored formats need to be + * refreshed. All formats which aren't originated from DataFormatter itself, i.e. all Formats added via + * {@link DataFormatter#addFormat(String, Format)} and {@link DataFormatter#setDefaultNumberFormat(Format)}, need to + * be added again. To notify callers, the returned {@link Observable} should be used. The Object in + * {@link Observer#update(Observable, Object)} is the new Locale. + * + * @return the listener object, where callers can register themselves + */ + public Observable getLocaleChangedObservable() { + return localeChangedObservable; + } + + /** + * Update formats when locale has been changed + * + * @param observable + * usually this is our own Observable instance + * @param localeObj + * only reacts on Locale objects + */ + public void update(Observable observable, Object localeObj) { + if (!(localeObj instanceof Locale)) + return; + Locale newLocale = (Locale)localeObj; + if (!localeIsAdapting || newLocale.equals(locale)) + return; + + locale = newLocale; + + dateSymbols = DateFormatSymbols.getInstance(locale); + decimalSymbols = DecimalFormatSymbols.getInstance(locale); + generalNumberFormat = new ExcelGeneralNumberFormat(locale); + + // taken from Date.toString() + defaultDateformat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", dateSymbols); + defaultDateformat.setTimeZone(LocaleUtil.getUserTimeZone()); + + // init built-in formats + + formats.clear(); + Format zipFormat = ZipPlusFourFormat.instance; + addFormat("00000\\-0000", zipFormat); + addFormat("00000-0000", zipFormat); + + Format phoneFormat = PhoneFormat.instance; + // allow for format string variations + addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); + addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat); + addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); + addFormat("###-####;(###) ###-####", phoneFormat); + + Format ssnFormat = SSNFormat.instance; + addFormat("000\\-00\\-0000", ssnFormat); + addFormat("000-00-0000", ssnFormat); + } + + /** + * Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class SSNFormat extends Format { + public static final Format instance = new SSNFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private SSNFormat() { + // enforce singleton + } + + /** 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); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class ZipPlusFourFormat extends Format { + public static final Format instance = new ZipPlusFourFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private ZipPlusFourFormat() { + // enforce singleton + } + + /** 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); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class PhoneFormat extends Format { + public static final Format instance = new PhoneFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + + private PhoneFormat() { + // enforce singleton + } + + /** Format a number as a phone number */ + public static String format(Number num) { + String result = df.format(num); + StringBuilder sb = new StringBuilder(); + String seg1, seg2, seg3; + int len = result.length(); + if (len <= 4) { + return result; + } + + seg3 = result.substring(len - 4, len); + seg2 = result.substring(Math.max(0, len - 7), len - 4); + seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7)); + + if (seg1.trim().length() > 0) { + sb.append('(').append(seg1).append(") "); + } + if (seg2.trim().length() > 0) { + sb.append(seg2).append('-'); + } + sb.append(seg3); + return sb.toString(); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class that does nothing and always returns a constant string. + * + * This format is used to simulate Excel's handling of a format string of all # when the value is 0. Excel will + * output "", Java will output "0". + * + */ + @SuppressWarnings("serial") + private static final class ConstantStringFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + private final String str; + + public ConstantStringFormat(String s) { + str = s; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(str); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Workaround until we merge {@link DataFormatter} with {@link CellFormat}. Constant, non-cachable wrapper around a + * {@link CellFormatResult} + */ + @SuppressWarnings("serial") + private final class CellFormatResultWrapper extends Format { + private final CellFormatResult result; + + private CellFormatResultWrapper(CellFormatResult result) { + this.result = result; + } + + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + if (emulateCSV) { + return toAppendTo.append(result.text); + } else { + return toAppendTo.append(result.text.trim()); + } + } + + public Object parseObject(String source, ParsePosition pos) { + return null; // Not supported + } + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java index 3a70d0d..fa41704 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -2,12 +2,9 @@ package com.alibaba.easyexcel.test.temp.poi; import java.io.FileOutputStream; import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.math.BigDecimal; import java.util.regex.Pattern; -import org.apache.poi.ss.formula.functions.T; import org.apache.poi.xssf.streaming.SXSSFCell; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; @@ -17,11 +14,8 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.metadata.CellData; import com.alibaba.fastjson.JSON; -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; - /** * 测试poi * @@ -42,9 +36,20 @@ public class PoiWriteTest { cell1.setCellValue(999999999999999L); SXSSFCell cell2 = row.createCell(1); cell2.setCellValue(1000000000000001L); + SXSSFCell cell32 = row.createCell(2); + cell32.setCellValue(300.35f); sxxsFWorkbook.write(fileOutputStream); } + @Test + public void write01() throws IOException { + float ff = 300.35f; + BigDecimal bd = new BigDecimal(Float.toString(ff)); + System.out.println(bd.doubleValue()); + System.out.println(bd.floatValue()); + + } + @Test public void write() throws IOException { FileOutputStream fileOutputStream = @@ -99,58 +104,4 @@ public class PoiWriteTest { } - @Test - public void test() throws Exception { - Class clazz = TestCell.class; - - Field field = clazz.getDeclaredField("c2"); - // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 - - Type gType = field.getGenericType(); - // 获得field的泛型类型 - - // 如果gType是ParameterizedType对象(参数化) - if (gType instanceof ParameterizedType) { - - ParameterizedType pType = (ParameterizedType)gType; - // 就把它转换成ParameterizedType对象 - - Type[] tArgs = pType.getActualTypeArguments(); - // 获得泛型类型的泛型参数(实际类型参数) - ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; - Class ttt = c.getRawType(); - System.out.println(ttt); - } else { - System.out.println("出错!!!"); - } - - } - - @Test - public void test2() throws Exception { - Class clazz = TestCell.class; - - Field field = clazz.getDeclaredField("c2"); - // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 - - Type gType = field.getGenericType(); - // 获得field的泛型类型 - - // 如果gType是ParameterizedType对象(参数化) - if (gType instanceof ParameterizedType) { - - ParameterizedType pType = (ParameterizedType)gType; - // 就把它转换成ParameterizedType对象 - - Type[] tArgs = pType.getActualTypeArguments(); - // 获得泛型类型的泛型参数(实际类型参数) - ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; - Class ttt = c.getRawType(); - System.out.println(ttt); - } else { - System.out.println("出错!!!"); - } - - } - } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java new file mode 100644 index 0000000..22de395 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java @@ -0,0 +1,34 @@ +package com.alibaba.easyexcel.test.temp.read; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.fastjson.JSON; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ +@Ignore +public class CommentTest { + private static final Logger LOGGER = LoggerFactory.getLogger(CommentTest.class); + + @Test + public void comment() throws Exception { + File file = new File("D:\\test\\d1.xlsx"); + List> datas = EasyExcel.read(file).doReadAllSync(); + for (Map data : datas) { + LOGGER.info("数据:{}", JSON.toJSONString(data)); + } + } + +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java new file mode 100644 index 0000000..a9ba7fa --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import lombok.Data; + +/** + * TODO + * + * @author Jiaju Zhuang + **/ +@Data +public class JsonData { + private String SS1; + private String sS2; + private String ss3; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java index b19ac81..6157181 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java @@ -14,6 +14,7 @@ import com.alibaba.easyexcel.test.core.large.LargeData; import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson.JSON; import net.sf.cglib.beans.BeanMap; @@ -43,7 +44,35 @@ public class Wirte { String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 - EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); + EasyExcel.write(fileName, DemoData.class).relativeHeadRowIndex(10).sheet("模板").doWrite(data()); + } + + @Test + public void simpleWrite2() { + // 写法1 + String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, WriteData.class).sheet("模板").doWrite(data1()); + } + + @Test + public void json() { + JsonData jsonData = new JsonData(); + jsonData.setSS1("11"); + jsonData.setSS2("22"); + jsonData.setSs3("33"); + System.out.println(JSON.toJSONString(jsonData)); + + } + + @Test + public void json3() { + String json = "{\"SS1\":\"11\",\"sS2\":\"22\",\"ss3\":\"33\"}"; + + JsonData jsonData = JSON.parseObject(json, JsonData.class); + System.out.println(JSON.toJSONString(jsonData)); + } private List> head() { @@ -64,7 +93,7 @@ public class Wirte { List list = new ArrayList(); for (int i = 0; i < 10; i++) { DemoData data = new DemoData(); - data.setString("字符串" + i); + data.setString("640121807369666560" + i); data.setDate(new Date()); data.setDoubleData(null); list.add(data); @@ -72,4 +101,14 @@ public class Wirte { return list; } + private List data1() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + WriteData data = new WriteData(); + data.setF(300.35f); + list.add(data); + } + return list; + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java new file mode 100644 index 0000000..b8ff14d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java @@ -0,0 +1,13 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import lombok.Data; + +/** + * write data + * + * @author Jiaju Zhuang + **/ +@Data +public class WriteData { + private float f; +} diff --git a/src/test/resources/dataformat/dataformat.xls b/src/test/resources/dataformat/dataformat.xls new file mode 100644 index 0000000..632cb1e Binary files /dev/null and b/src/test/resources/dataformat/dataformat.xls differ diff --git a/src/test/resources/dataformat/dataformat.xlsx b/src/test/resources/dataformat/dataformat.xlsx new file mode 100644 index 0000000..9f41f27 Binary files /dev/null and b/src/test/resources/dataformat/dataformat.xlsx differ diff --git a/src/test/resources/demo/demo.xlsx b/src/test/resources/demo/demo.xlsx index 303733c..662bb97 100644 Binary files a/src/test/resources/demo/demo.xlsx and b/src/test/resources/demo/demo.xlsx differ diff --git a/src/test/resources/demo/extra.xlsx b/src/test/resources/demo/extra.xlsx new file mode 100644 index 0000000..4936b05 Binary files /dev/null and b/src/test/resources/demo/extra.xlsx differ diff --git a/src/test/resources/demo/fill/composite.xlsx b/src/test/resources/demo/fill/composite.xlsx new file mode 100644 index 0000000..c76a2b1 Binary files /dev/null and b/src/test/resources/demo/fill/composite.xlsx differ diff --git a/src/test/resources/extra/extra.xls b/src/test/resources/extra/extra.xls new file mode 100644 index 0000000..89f389b Binary files /dev/null and b/src/test/resources/extra/extra.xls differ diff --git a/src/test/resources/extra/extra.xlsx b/src/test/resources/extra/extra.xlsx new file mode 100644 index 0000000..4936b05 Binary files /dev/null and b/src/test/resources/extra/extra.xlsx differ diff --git a/src/test/resources/fill/composite.xls b/src/test/resources/fill/composite.xls new file mode 100644 index 0000000..e48aa0c Binary files /dev/null and b/src/test/resources/fill/composite.xls differ diff --git a/src/test/resources/fill/composite.xlsx b/src/test/resources/fill/composite.xlsx new file mode 100644 index 0000000..c76a2b1 Binary files /dev/null and b/src/test/resources/fill/composite.xlsx differ diff --git a/update.md b/update.md index 864a780..834bee6 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,20 @@ +# 2.2.0-beta1 +* 重写主流程,代码更加优雅 +* 修复用String接收日期、数字和excel显示不一致的bug(不是完美修复,但是大部分情况已经兼容) +* 降低Ehcache版本 3.7.1(jkd7) -> 3.4.0(jdk6) +* 修复xls 用Map接收时多次接收会是同一个对象的bug +* 修复浮点型数据导入到excel 会丢失精度的bug +* 新增支持读取批注、超链接、合并单元格 +* 如果是`RuntimeException`则不再封装对象 +* 新增`CellData`可以获取行列号 +* 新增样式注解 +* 新增合并单元格注解 +* 提升合并策略效率 +* 兼容部分比较特殊的excel +* 同时传入了`List>`和`class`的head,会通过index去匹配注解 +* 修复读取转换器的并发问题 +* 填充支持多个List对象 + # 2.1.6 * 修复写入只有`sheetName`会抛异常