+ * For all characters which cannot be represented in XML as defined by the XML 1.0 specification,
+ * the characters are escaped using the Unicode numerical character representation escape character
+ * format _xHHHH_, where H represents a hexadecimal character in the character's value.
+ *
+ * Example: The Unicode character 0D is invalid in an XML 1.0 document,
+ * so it shall be escaped as _x000D_.
+ *
+ * See section 3.18.9 in the OOXML spec.
+ * @see org.apache.poi.xssf.usermodel.XSSFRichTextString#utfDecode(String)
+ */
+ static String utfDecode(String value) {
+ if (value == null || !value.contains("_x")) {
+ return value;
+ }
+
+ StringBuilder buf = new StringBuilder();
+ Matcher m = UTF_PATTTERN.matcher(value);
+ int idx = 0;
+ while (m.find()) {
+ int pos = m.start();
+ if (pos > idx) {
+ buf.append(value, idx, pos);
+ }
+
+ String code = m.group(1);
+ int icode = Integer.decode("0x" + code);
+ buf.append((char)icode);
+
+ idx = m.end();
+ }
+
+ // small optimization: don't go via StringBuilder if not necessary,
+ // the encodings are very rare, so we should almost always go via this shortcut.
+ if (idx == 0) {
+ return value;
+ }
+
+ buf.append(value.substring(idx));
+ return buf.toString();
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java
new file mode 100644
index 000000000..7b48c8a62
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java
@@ -0,0 +1,103 @@
+package com.alibaba.excel.analysis.v07.handlers.sax;
+
+import java.util.HashMap;
+import java.util.Map;
+
+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;
+
+import lombok.extern.slf4j.Slf4j;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * @author jipengfei
+ */
+@Slf4j
+public class XlsxRowHandler extends DefaultHandler {
+ private final XlsxReadContext xlsxReadContext;
+ private static final Map XLSX_CELL_HANDLER_MAP = new HashMap<>(64);
+
+ 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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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);
+ XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_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/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java
new file mode 100644
index 000000000..3d3ce63c0
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java
@@ -0,0 +1,17 @@
+package com.alibaba.excel.annotation;
+
+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;
+
+/**
+ * Ignore convert excel
+ *
+ * @author Jiaju Zhuang
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelIgnore {}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java
new file mode 100644
index 000000000..1475ad3a6
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java
@@ -0,0 +1,18 @@
+package com.alibaba.excel.annotation;
+
+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;
+
+/**
+ * Ignore all unannotated fields.
+ *
+ * @author Jiaju Zhuang
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelIgnoreUnannotated {
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java
new file mode 100644
index 000000000..db5392660
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java
@@ -0,0 +1,68 @@
+package com.alibaba.excel.annotation;
+
+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 com.alibaba.excel.converters.AutoConverter;
+import com.alibaba.excel.converters.Converter;
+
+/**
+ * @author jipengfei
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ExcelProperty {
+
+ /**
+ * The name of the sheet header.
+ *
+ *
+ * write: It automatically merges when you have more than one head
+ *
+ * read: When you have multiple heads, take the last one
+ *
+ * @return The name of the sheet header
+ */
+ String[] value() default {""};
+
+ /**
+ * Index of column
+ *
+ * Read or write it on the index of column, If it's equal to -1, it's sorted by Java class.
+ *
+ * priority: index > order > default sort
+ *
+ * @return Index of column
+ */
+ int index() default -1;
+
+ /**
+ * Defines the sort order for an column.
+ *
+ * priority: index > order > default sort
+ *
+ * @return Order of column
+ */
+ int order() default Integer.MAX_VALUE;
+
+ /**
+ * Force the current field to use this converter.
+ *
+ * @return Converter
+ */
+ Class extends Converter>> converter() default AutoConverter.class;
+
+ /**
+ *
+ * default @see com.alibaba.excel.util.TypeUtil if default is not meet you can set format
+ *
+ * @return Format string
+ * @deprecated please use {@link com.alibaba.excel.annotation.format.DateTimeFormat}
+ */
+ @Deprecated
+ String format() default "";
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java
new file mode 100644
index 000000000..fefcededf
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java
@@ -0,0 +1,40 @@
+package com.alibaba.excel.annotation.format;
+
+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 com.alibaba.excel.enums.BooleanEnum;
+
+/**
+ * Convert date format.
+ *
+ *
+ * write: It can be used on classes {@link java.util.Date}
+ *
+ * read: It can be used on classes {@link String}
+ *
+ * @author Jiaju Zhuang
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DateTimeFormat {
+
+ /**
+ *
+ * Specific format reference {@link java.text.SimpleDateFormat}
+ *
+ * @return Format pattern
+ */
+ String value() default "";
+
+ /**
+ * True if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * @return True if date uses 1904 windowing, or false if using 1900 date windowing.
+ */
+ BooleanEnum use1904windowing() default BooleanEnum.DEFAULT;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java
new file mode 100644
index 000000000..a58ef4fb4
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java
@@ -0,0 +1,39 @@
+package com.alibaba.excel.annotation.format;
+
+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 java.math.RoundingMode;
+
+/**
+ * Convert number format.
+ *
+ *
+ * write: It can be used on classes that inherit {@link Number}
+ *
+ * read: It can be used on classes {@link String}
+ *
+ * @author Jiaju Zhuang
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface NumberFormat {
+
+ /**
+ *
+ * Specific format reference {@link java.text.DecimalFormat}
+ *
+ * @return Format pattern
+ */
+ String value() default "";
+
+ /**
+ * Rounded by default
+ *
+ * @return RoundingMode
+ */
+ RoundingMode roundingMode() default RoundingMode.HALF_UP;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java
new file mode 100644
index 000000000..00306cc0c
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java
@@ -0,0 +1,27 @@
+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;
+
+/**
+ * Set the width of the table
+ *
+ * @author Jiaju Zhuang
+ */
+@Target({ElementType.FIELD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ColumnWidth {
+
+ /**
+ * Column width
+ *
+ * -1 means the default column width is used
+ *
+ * @return Column width
+ */
+ int value() default -1;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java
new file mode 100644
index 000000000..fd890d481
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java
@@ -0,0 +1,91 @@
+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 com.alibaba.excel.enums.BooleanEnum;
+
+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
+ */
+ BooleanEnum italic() default BooleanEnum.DEFAULT;
+
+ /**
+ * Whether to use a strikeout horizontal line through the text or not
+ */
+ BooleanEnum strikeout() default BooleanEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum bold() default BooleanEnum.DEFAULT;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java
new file mode 100644
index 000000000..60654fe55
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java
new file mode 100644
index 000000000..8f338e86b
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java
@@ -0,0 +1,27 @@
+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;
+
+/**
+ * Set the height of each table
+ *
+ * @author Jiaju Zhuang
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ContentRowHeight {
+
+ /**
+ * Set the content height
+ *
+ * -1 mean the auto set height
+ *
+ * @return Content height
+ */
+ short value() default -1;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java
new file mode 100644
index 000000000..bfa8aa4f7
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java
@@ -0,0 +1,162 @@
+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 com.alibaba.excel.enums.BooleanEnum;
+import com.alibaba.excel.enums.poi.BorderStyleEnum;
+import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
+import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
+import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
+
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.IgnoredErrorType;
+import org.apache.poi.ss.usermodel.IndexedColors;
+
+/**
+ * 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
+ */
+ BooleanEnum hidden() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the cell's using this style to be locked
+ */
+ BooleanEnum locked() default BooleanEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum quotePrefix() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the type of horizontal alignment for the cell
+ */
+ HorizontalAlignmentEnum horizontalAlignment() default HorizontalAlignmentEnum.DEFAULT;
+
+ /**
+ * 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
+ *
+ */
+ BooleanEnum wrapped() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the type of vertical alignment for the cell
+ */
+ VerticalAlignmentEnum verticalAlignment() default VerticalAlignmentEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BorderStyleEnum borderLeft() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the right border of the cell
+ */
+ BorderStyleEnum borderRight() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the top border of the cell
+ */
+ BorderStyleEnum borderTop() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the bottom border of the cell
+ */
+ BorderStyleEnum borderBottom() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ FillPatternTypeEnum fillPatternType() default FillPatternTypeEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum shrinkToFit() default BooleanEnum.DEFAULT;
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java
new file mode 100644
index 000000000..e7f0e1ba3
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java
@@ -0,0 +1,91 @@
+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 com.alibaba.excel.enums.BooleanEnum;
+
+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 -1;
+
+ /**
+ * Whether to use italics or not
+ */
+ BooleanEnum italic() default BooleanEnum.DEFAULT;
+
+ /**
+ * Whether to use a strikeout horizontal line through the text or not
+ */
+ BooleanEnum strikeout() default BooleanEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum bold() default BooleanEnum.DEFAULT;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java
new file mode 100644
index 000000000..828d2c06e
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java
@@ -0,0 +1,26 @@
+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;
+
+/**
+ * Set the height of each table
+ *
+ * @author Jiaju Zhuang
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface HeadRowHeight {
+ /**
+ * Set the header height
+ *
+ * -1 mean the auto set height
+ *
+ * @return Header height
+ */
+ short value() default -1;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java
new file mode 100644
index 000000000..85900eac5
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java
@@ -0,0 +1,156 @@
+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 com.alibaba.excel.enums.BooleanEnum;
+import com.alibaba.excel.enums.poi.BorderStyleEnum;
+import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
+import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
+import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
+
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.IgnoredErrorType;
+import org.apache.poi.ss.usermodel.IndexedColors;
+
+/**
+ * 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
+ */
+ BooleanEnum hidden() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the cell's using this style to be locked
+ */
+ BooleanEnum locked() default BooleanEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum quotePrefix() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the type of horizontal alignment for the cell
+ */
+ HorizontalAlignmentEnum horizontalAlignment() default HorizontalAlignmentEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum wrapped() default BooleanEnum.DEFAULT;
+
+ /**
+ * Set the type of vertical alignment for the cell
+ */
+ VerticalAlignmentEnum verticalAlignment() default VerticalAlignmentEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BorderStyleEnum borderLeft() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the right border of the cell
+ */
+ BorderStyleEnum borderRight() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the top border of the cell
+ */
+ BorderStyleEnum borderTop() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * Set the type of border to use for the bottom border of the cell
+ */
+ BorderStyleEnum borderBottom() default BorderStyleEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ FillPatternTypeEnum fillPatternType() default FillPatternTypeEnum.DEFAULT;
+
+ /**
+ * 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
+ */
+ BooleanEnum shrinkToFit() default BooleanEnum.DEFAULT;
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java
new file mode 100644
index 000000000..6c3a14806
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java
new file mode 100644
index 000000000..6387d2a11
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java
@@ -0,0 +1,164 @@
+package com.alibaba.excel.cache;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Optional;
+import java.util.UUID;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.util.FileUtils;
+import com.alibaba.excel.util.ListUtils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.ehcache.CacheManager;
+import org.ehcache.config.CacheConfiguration;
+import org.ehcache.config.builders.CacheConfigurationBuilder;
+import org.ehcache.config.builders.CacheManagerBuilder;
+import org.ehcache.config.builders.ResourcePoolsBuilder;
+import org.ehcache.config.units.EntryUnit;
+import org.ehcache.config.units.MemoryUnit;
+
+/**
+ * Default cache
+ *
+ * @author Jiaju Zhuang
+ */
+@Slf4j
+public class Ehcache implements ReadCache {
+ public static final int BATCH_COUNT = 100;
+ /**
+ * Key index
+ */
+ private int activeIndex = 0;
+ public static final int DEBUG_CACHE_MISS_SIZE = 1000;
+ public static final int DEBUG_WRITE_SIZE = 100 * 10000;
+ private ArrayList dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ private static final CacheManager FILE_CACHE_MANAGER;
+ private static final CacheConfiguration FILE_CACHE_CONFIGURATION;
+ private static final CacheManager ACTIVE_CACHE_MANAGER;
+ private static final File CACHE_PATH_FILE;
+
+ private final CacheConfiguration activeCacheConfiguration;
+ /**
+ * Bulk storage data
+ */
+ private org.ehcache.Cache fileCache;
+ /**
+ * Currently active cache
+ */
+ private org.ehcache.Cache activeCache;
+ private String cacheAlias;
+ /**
+ * Count the number of cache misses
+ */
+ private int cacheMiss = 0;
+
+ @Deprecated
+ public Ehcache(Integer maxCacheActivateSize) {
+ this(maxCacheActivateSize, null);
+ }
+
+ public Ehcache(Integer maxCacheActivateSize, Integer maxCacheActivateBatchCount) {
+ // In order to be compatible with the code
+ // If the user set up `maxCacheActivateSize`, then continue using it
+ if (maxCacheActivateSize != null) {
+ this.activeCacheConfiguration = CacheConfigurationBuilder
+ .newCacheConfigurationBuilder(Integer.class, ArrayList.class,
+ ResourcePoolsBuilder.newResourcePoolsBuilder()
+ .heap(maxCacheActivateSize, MemoryUnit.MB))
+ .build();
+ } else {
+ this.activeCacheConfiguration = CacheConfigurationBuilder
+ .newCacheConfigurationBuilder(Integer.class, ArrayList.class,
+ ResourcePoolsBuilder.newResourcePoolsBuilder()
+ .heap(maxCacheActivateBatchCount, EntryUnit.ENTRIES))
+ .build();
+ }
+ }
+
+ static {
+ CACHE_PATH_FILE = FileUtils.createCacheTmpFile();
+ FILE_CACHE_MANAGER =
+ CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(CACHE_PATH_FILE)).build(
+ true);
+ ACTIVE_CACHE_MANAGER = CacheManagerBuilder.newCacheManagerBuilder().build(true);
+ FILE_CACHE_CONFIGURATION = CacheConfigurationBuilder
+ .newCacheConfigurationBuilder(Integer.class, ArrayList.class, ResourcePoolsBuilder.newResourcePoolsBuilder()
+ .disk(20, MemoryUnit.GB)).build();
+ }
+
+ @Override
+ public void init(AnalysisContext analysisContext) {
+ cacheAlias = UUID.randomUUID().toString();
+ try {
+ fileCache = FILE_CACHE_MANAGER.createCache(cacheAlias, FILE_CACHE_CONFIGURATION);
+ } catch (IllegalStateException e) {
+ //fix Issue #2693,Temporary files may be deleted if there is no operation for a long time, so they need
+ // to be recreated.
+ if (CACHE_PATH_FILE.exists()) {
+ throw e;
+ }
+ synchronized (Ehcache.class) {
+ if (!CACHE_PATH_FILE.exists()) {
+ if (log.isDebugEnabled()) {
+ log.debug("cache file dir is not exist retry create");
+ }
+ FileUtils.createDirectory(CACHE_PATH_FILE);
+ }
+ }
+ fileCache = FILE_CACHE_MANAGER.createCache(cacheAlias, FILE_CACHE_CONFIGURATION);
+ }
+ activeCache = ACTIVE_CACHE_MANAGER.createCache(cacheAlias, activeCacheConfiguration);
+ }
+
+ @Override
+ public void put(String value) {
+ dataList.add(value);
+ if (dataList.size() >= BATCH_COUNT) {
+ fileCache.put(activeIndex, dataList);
+ activeIndex++;
+ dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ }
+ if (log.isDebugEnabled()) {
+ int alreadyPut = activeIndex * BATCH_COUNT + dataList.size();
+ if (alreadyPut % DEBUG_WRITE_SIZE == 0) {
+ log.debug("Already put :{}", alreadyPut);
+ }
+ }
+ }
+
+ @Override
+ public String get(Integer key) {
+ if (key == null || key < 0) {
+ return null;
+ }
+ int route = key / BATCH_COUNT;
+ ArrayList dataList = activeCache.get(route);
+ if (dataList == null) {
+ dataList = fileCache.get(route);
+ activeCache.put(route, dataList);
+ if (log.isDebugEnabled()) {
+ if (cacheMiss++ % DEBUG_CACHE_MISS_SIZE == 0) {
+ log.debug("Cache misses count:{}", cacheMiss);
+ }
+ }
+ }
+ return dataList.get(key % BATCH_COUNT);
+ }
+
+ @Override
+ public void putFinished() {
+ if (CollectionUtils.isEmpty(dataList)) {
+ return;
+ }
+ fileCache.put(activeIndex, dataList);
+ }
+
+ @Override
+ public void destroy() {
+ FILE_CACHE_MANAGER.removeCache(cacheAlias);
+ ACTIVE_CACHE_MANAGER.removeCache(cacheAlias);
+ }
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java
new file mode 100644
index 000000000..82ada960a
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java
@@ -0,0 +1,38 @@
+package com.alibaba.excel.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.excel.context.AnalysisContext;
+
+/**
+ * Putting temporary data directly into a map is a little more efficient but very memory intensive
+ *
+ * @author Jiaju Zhuang
+ */
+public class MapCache implements ReadCache {
+ private final List cache = new ArrayList<>();
+
+ @Override
+ public void init(AnalysisContext analysisContext) {}
+
+ @Override
+ public void put(String value) {
+ cache.add(value);
+ }
+
+ @Override
+ public String get(Integer key) {
+ if (key == null || key < 0) {
+ return null;
+ }
+ return cache.get(key);
+ }
+
+ @Override
+ public void putFinished() {}
+
+ @Override
+ public void destroy() {}
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java
new file mode 100644
index 000000000..48072fae2
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java
@@ -0,0 +1,47 @@
+package com.alibaba.excel.cache;
+
+import com.alibaba.excel.context.AnalysisContext;
+
+/**
+ * Read cache
+ *
+ * @author Jiaju Zhuang
+ */
+public interface ReadCache {
+
+ /**
+ * Initialize cache
+ *
+ * @param analysisContext
+ * A context is the main anchorage point of a excel reader.
+ */
+ void init(AnalysisContext analysisContext);
+
+ /**
+ * Automatically generate the key and put it in the cache.Key start from 0
+ *
+ * @param value
+ * Cache value
+ */
+ void put(String value);
+
+ /**
+ * Get value
+ *
+ * @param key
+ * Index
+ * @return Value
+ */
+ String get(Integer key);
+
+ /**
+ * It's called when all the values are put in
+ */
+ void putFinished();
+
+ /**
+ * Called when the excel read is complete
+ */
+ void destroy();
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/XlsCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/XlsCache.java
new file mode 100644
index 000000000..261bc1875
--- /dev/null
+++ b/easyexcel-core/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 final 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/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java
new file mode 100644
index 000000000..9730dc08a
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java
@@ -0,0 +1,23 @@
+package com.alibaba.excel.cache.selector;
+
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+import com.alibaba.excel.cache.ReadCache;
+
+/**
+ * Choose a eternal cache
+ *
+ * @author Jiaju Zhuang
+ **/
+public class EternalReadCacheSelector implements ReadCacheSelector {
+ private ReadCache readCache;
+
+ public EternalReadCacheSelector(ReadCache readCache) {
+ this.readCache = readCache;
+ }
+
+ @Override
+ public ReadCache readCache(PackagePart sharedStringsTablePackagePart) {
+ return readCache;
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java
new file mode 100644
index 000000000..3a2e50246
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java
@@ -0,0 +1,21 @@
+package com.alibaba.excel.cache.selector;
+
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+import com.alibaba.excel.cache.ReadCache;
+
+/**
+ * Select the cache
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface ReadCacheSelector {
+
+ /**
+ * Select a cache
+ *
+ * @param sharedStringsTablePackagePart
+ * @return
+ */
+ ReadCache readCache(PackagePart sharedStringsTablePackagePart);
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java
new file mode 100644
index 000000000..278c63bec
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java
@@ -0,0 +1,112 @@
+package com.alibaba.excel.cache.selector;
+
+import java.io.IOException;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.excel.cache.Ehcache;
+import com.alibaba.excel.cache.MapCache;
+import com.alibaba.excel.cache.ReadCache;
+
+/**
+ * Simple cache selector
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class SimpleReadCacheSelector implements ReadCacheSelector {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReadCacheSelector.class);
+ /**
+ * Convert bytes to megabytes
+ */
+ private static final long B2M = 1000 * 1000L;
+ /**
+ * If it's less than 5M, use map cache, or use ehcache.unit MB.
+ */
+ private static final long DEFAULT_MAX_USE_MAP_CACHE_SIZE = 5;
+
+ /**
+ * Maximum batch of `SharedStrings` stored in memory.
+ * The batch size is 100.{@link Ehcache#BATCH_COUNT}
+ */
+ private static final int DEFAULT_MAX_EHCACHE_ACTIVATE_BATCH_COUNT = 20;
+
+ /**
+ * Shared strings exceeding this value will use {@link Ehcache},or use {@link MapCache}.unit MB.
+ */
+ private Long maxUseMapCacheSize;
+
+ /**
+ * Maximum size of cache activation.unit MB.
+ *
+ * @deprecated Please use maxCacheActivateBatchCount to control the size of the occupied memory
+ */
+ @Deprecated
+ private Integer maxCacheActivateSize;
+
+ /**
+ * Maximum batch of `SharedStrings` stored in memory.
+ * The batch size is 100.{@link Ehcache#BATCH_COUNT}
+ */
+ private Integer maxCacheActivateBatchCount;
+
+ public SimpleReadCacheSelector() {
+ }
+
+ /**
+ * Parameter maxCacheActivateSize has already been abandoned
+ *
+ * @param maxUseMapCacheSize
+ * @param maxCacheActivateSize
+ */
+ @Deprecated
+ public SimpleReadCacheSelector(Long maxUseMapCacheSize, Integer maxCacheActivateSize) {
+ this.maxUseMapCacheSize = maxUseMapCacheSize;
+ this.maxCacheActivateSize = maxCacheActivateSize;
+ }
+
+ @Override
+ public ReadCache readCache(PackagePart sharedStringsTablePackagePart) {
+ long size = sharedStringsTablePackagePart.getSize();
+ if (size < 0) {
+ try {
+ size = sharedStringsTablePackagePart.getInputStream().available();
+ } catch (IOException e) {
+ LOGGER.warn("Unable to get file size, default used MapCache");
+ return new MapCache();
+ }
+ }
+ if (maxUseMapCacheSize == null) {
+ maxUseMapCacheSize = DEFAULT_MAX_USE_MAP_CACHE_SIZE;
+ }
+ if (size < maxUseMapCacheSize * B2M) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Use map cache.size:{}", size);
+ }
+ return new MapCache();
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Use ehcache.size:{}", size);
+ }
+
+ // In order to be compatible with the code
+ // If the user set up `maxCacheActivateSize`, then continue using it
+ if (maxCacheActivateSize != null) {
+ return new Ehcache(maxCacheActivateSize, maxCacheActivateBatchCount);
+ } else {
+ if (maxCacheActivateBatchCount == null) {
+ maxCacheActivateBatchCount = DEFAULT_MAX_EHCACHE_ACTIVATE_BATCH_COUNT;
+ }
+ return new Ehcache(maxCacheActivateSize, maxCacheActivateBatchCount);
+ }
+
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java
new file mode 100644
index 000000000..b6fb8e481
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java
@@ -0,0 +1,530 @@
+package com.alibaba.excel.constant;
+
+import java.util.Locale;
+import java.util.Map;
+
+import com.alibaba.excel.util.MapUtils;
+import com.alibaba.excel.util.StringUtils;
+
+/**
+ * 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 RESERVED = "reserved-";
+
+ public static short GENERAL = 0;
+
+ public static final String[] BUILTIN_FORMATS_ALL_LANGUAGES = {
+ // 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-36 No specific correspondence found in the official documentation.
+ // 23
+ null,
+ // 24
+ null,
+ // 25
+ null,
+ // 26
+ null,
+ // 27
+ null,
+ // 28
+ null,
+ // 29
+ null,
+ // 30
+ null,
+ // 31
+ null,
+ // 32
+ null,
+ // 33
+ null,
+ // 34
+ null,
+ // 35
+ null,
+ // 36
+ null,
+ // 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
+ "@",
+ };
+
+ public 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
+ };
+
+ public 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 final Map BUILTIN_FORMATS_MAP_CN = buildMap(BUILTIN_FORMATS_CN);
+ public static final Map BUILTIN_FORMATS_MAP_US = buildMap(BUILTIN_FORMATS_US);
+ public static final short MIN_CUSTOM_DATA_FORMAT_INDEX = 82;
+
+ public static String getBuiltinFormat(Short index, String defaultFormat, Locale locale) {
+ if (index == null || index <= 0) {
+ return defaultFormat;
+ }
+
+ // Give priority to checking if it is the default value for all languages
+ if (index < BUILTIN_FORMATS_ALL_LANGUAGES.length) {
+ String format = BUILTIN_FORMATS_ALL_LANGUAGES[index];
+ if (format != null) {
+ return format;
+ }
+ }
+
+ // In other cases, give priority to using the externally provided format
+ if (!StringUtils.isEmpty(defaultFormat) && !defaultFormat.startsWith(RESERVED)) {
+ return defaultFormat;
+ }
+
+ // Finally, try using the built-in format
+ String[] builtinFormat = switchBuiltinFormats(locale);
+ if (index >= builtinFormat.length) {
+ return defaultFormat;
+ }
+ return builtinFormat[index];
+ }
+
+ public static String[] switchBuiltinFormats(Locale locale) {
+ if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) {
+ return BUILTIN_FORMATS_US;
+ }
+ return BUILTIN_FORMATS_CN;
+ }
+
+ public static Map switchBuiltinFormatsMap(Locale locale) {
+ if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) {
+ return BUILTIN_FORMATS_MAP_US;
+ }
+ return BUILTIN_FORMATS_MAP_CN;
+ }
+
+ private static Map buildMap(String[] builtinFormats) {
+ Map map = MapUtils.newHashMapWithExpectedSize(builtinFormats.length);
+ for (int i = 0; i < builtinFormats.length; i++) {
+ map.put(builtinFormats[i], (short)i);
+ }
+ return map;
+ }
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java
new file mode 100644
index 000000000..cdc414fa9
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java
@@ -0,0 +1,19 @@
+package com.alibaba.excel.constant;
+
+import java.math.MathContext;
+import java.math.RoundingMode;
+
+/**
+ * Used to store constant
+ *
+ * @author Jiaju Zhuang
+ */
+public class EasyExcelConstants {
+
+ /**
+ * Excel by default with 15 to store Numbers, and the double in Java can use to store number 17, led to the accuracy
+ * will be a problem. So you need to set up 15 to deal with precision
+ */
+ public static final MathContext EXCEL_MATH_CONTEXT = new MathContext(15, RoundingMode.HALF_UP);
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java
new file mode 100644
index 000000000..c1d335924
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java
@@ -0,0 +1,99 @@
+package com.alibaba.excel.constant;
+
+/**
+ * @author jipengfei
+ */
+public class ExcelXmlConstants {
+ public static final String DIMENSION_TAG = "dimension";
+ public static final String ROW_TAG = "row";
+ 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 NS2_DIMENSION_TAG = "ns2:dimension";
+
+ public static final String X_ROW_TAG = "x:row";
+ public static final String NS2_ROW_TAG = "ns2:row";
+
+ public static final String X_CELL_FORMULA_TAG = "x:f";
+ public static final String NS2_CELL_FORMULA_TAG = "ns2:f";
+ public static final String X_CELL_VALUE_TAG = "x:v";
+ public static final String NS2_CELL_VALUE_TAG = "ns2: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 NS2_CELL_INLINE_STRING_VALUE_TAG = "ns2:t";
+
+ public static final String X_CELL_TAG = "x:c";
+ public static final String NS2_CELL_TAG = "ns2:c";
+ public static final String X_MERGE_CELL_TAG = "x:mergeCell";
+ public static final String NS2_MERGE_CELL_TAG = "ns2:mergeCell";
+ public static final String X_HYPERLINK_TAG = "x:hyperlink";
+ public static final String NS2_HYPERLINK_TAG = "ns2: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";
+
+ /**
+ * rId attribute
+ */
+ public static final String ATTRIBUTE_RID = "r:id";
+
+ /**
+ * Cell range split
+ */
+ public static final String CELL_RANGE_SPLIT = ":";
+
+ // The following is a constant read the `SharedStrings.xml`
+
+ /**
+ * text
+ */
+ public static final String SHAREDSTRINGS_T_TAG = "t";
+ public static final String SHAREDSTRINGS_X_T_TAG = "x:t";
+ public static final String SHAREDSTRINGS_NS2_T_TAG = "ns2:t";
+
+
+ /**
+ * SharedStringItem
+ */
+ public static final String SHAREDSTRINGS_SI_TAG = "si";
+ public static final String SHAREDSTRINGS_X_SI_TAG = "x:si";
+ public static final String SHAREDSTRINGS_NS2_SI_TAG = "ns2:si";
+
+
+ /**
+ * Mac 2016 2017 will have this extra field to ignore
+ */
+ public static final String SHAREDSTRINGS_RPH_TAG = "rPh";
+ public static final String SHAREDSTRINGS_X_RPH_TAG = "x:rPh";
+ public static final String SHAREDSTRINGS_NS2_RPH_TAG = "ns2:rPh";
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java
new file mode 100644
index 000000000..309b62971
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java
@@ -0,0 +1,34 @@
+package com.alibaba.excel.constant;
+
+/**
+ * Order constant.
+ *
+ * @author Jiaju Zhuang
+ */
+public class OrderConstant {
+
+ /**
+ * The system's own style
+ */
+ public static int DEFAULT_DEFINE_STYLE = -70000;
+
+ /**
+ * Annotation style definition
+ */
+ public static int ANNOTATION_DEFINE_STYLE = -60000;
+
+ /**
+ * Define style.
+ */
+ public static final int DEFINE_STYLE = -50000;
+
+ /**
+ * default order.
+ */
+ public static int DEFAULT_ORDER = 0;
+
+ /**
+ * Sorting of styles written to cells.
+ */
+ public static int FILL_STYLE = 50000;
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java
new file mode 100644
index 000000000..37039cbcf
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java
@@ -0,0 +1,147 @@
+package com.alibaba.excel.context;
+
+import java.io.InputStream;
+import java.util.List;
+
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.read.metadata.ReadSheet;
+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;
+
+/**
+ *
+ * A context is the main anchorage point of a excel reader.
+ *
+ * @author jipengfei
+ */
+public interface AnalysisContext {
+ /**
+ * Select the current table
+ *
+ * @param readSheet
+ * sheet to read
+ */
+ void currentSheet(ReadSheet readSheet);
+
+ /**
+ * All information about the workbook you are currently working on
+ *
+ * @return Current workbook holder
+ */
+ ReadWorkbookHolder readWorkbookHolder();
+
+ /**
+ * All information about the sheet you are currently working on
+ *
+ * @return Current sheet holder
+ */
+ ReadSheetHolder readSheetHolder();
+
+ /**
+ * Set row of currently operated cell
+ *
+ * @param readRowHolder
+ * Current row holder
+ */
+ void readRowHolder(ReadRowHolder readRowHolder);
+
+ /**
+ * Row of currently operated cell
+ *
+ * @return Current row holder
+ */
+ ReadRowHolder readRowHolder();
+
+ /**
+ * The current read operation corresponds to the readSheetHolder or readWorkbookHolder
+ *
+ * @return Current holder
+ */
+ ReadHolder currentReadHolder();
+
+ /**
+ * Custom attribute
+ *
+ * @return
+ */
+ 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 excel type
+ *
+ * @return excel type
+ * @deprecated please use {@link #readWorkbookHolder()}
+ */
+ @Deprecated
+ ExcelTypeEnum getExcelType();
+
+ /**
+ * get in io
+ *
+ * @return file io
+ * @deprecated please use {@link #readWorkbookHolder()}
+ */
+ @Deprecated
+ InputStream getInputStream();
+
+ /**
+ * get current row
+ *
+ * @return
+ * @deprecated please use {@link #readRowHolder()}
+ */
+ @Deprecated
+ Integer getCurrentRowNum();
+
+ /**
+ * get total row ,Data may be inaccurate
+ *
+ * @return
+ * @deprecated please use {@link #readRowHolder()}
+ */
+ @Deprecated
+ Integer getTotalCount();
+
+ /**
+ * get current result
+ *
+ * @return get current result
+ * @deprecated please use {@link #readRowHolder()}
+ */
+ @Deprecated
+ Object getCurrentRowAnalysisResult();
+
+ /**
+ * Interrupt execution
+ *
+ * @deprecated please use {@link AnalysisEventListener#hasNext(AnalysisContext)}
+ */
+ @Deprecated
+ void interrupt();
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java
new file mode 100644
index 000000000..5385831bd
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java
@@ -0,0 +1,174 @@
+package com.alibaba.excel.context;
+
+import java.io.InputStream;
+import java.util.List;
+
+import com.alibaba.excel.exception.ExcelAnalysisException;
+import com.alibaba.excel.read.metadata.ReadSheet;
+import com.alibaba.excel.read.metadata.ReadWorkbook;
+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.csv.CsvReadSheetHolder;
+import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
+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;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author jipengfei
+ */
+@Slf4j
+public class AnalysisContextImpl implements AnalysisContext {
+ /**
+ * The Workbook currently written
+ */
+ private ReadWorkbookHolder readWorkbookHolder;
+ /**
+ * Current sheet holder
+ */
+ private ReadSheetHolder readSheetHolder;
+ /**
+ * Current row holder
+ */
+ private ReadRowHolder readRowHolder;
+ /**
+ * Configuration of currently operated cell
+ */
+ private ReadHolder currentReadHolder;
+ /**
+ * Event processor
+ */
+ private final AnalysisEventProcessor analysisEventProcessor;
+
+ public AnalysisContextImpl(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) {
+ if (readWorkbook == null) {
+ throw new IllegalArgumentException("Workbook argument cannot be null");
+ }
+ switch (actualExcelType) {
+ case XLS:
+ readWorkbookHolder = new XlsReadWorkbookHolder(readWorkbook);
+ break;
+ case XLSX:
+ readWorkbookHolder = new XlsxReadWorkbookHolder(readWorkbook);
+ break;
+ case CSV:
+ readWorkbookHolder = new CsvReadWorkbookHolder(readWorkbook);
+ break;
+ default:
+ break;
+ }
+ currentReadHolder = readWorkbookHolder;
+ analysisEventProcessor = new DefaultAnalysisEventProcessor();
+ if (log.isDebugEnabled()) {
+ log.debug("Initialization 'AnalysisContextImpl' complete");
+ }
+ }
+
+ @Override
+ public void currentSheet(ReadSheet readSheet) {
+ switch (readWorkbookHolder.getExcelType()) {
+ case XLS:
+ readSheetHolder = new XlsReadSheetHolder(readSheet, readWorkbookHolder);
+ break;
+ case XLSX:
+ readSheetHolder = new XlsxReadSheetHolder(readSheet, readWorkbookHolder);
+ break;
+ case CSV:
+ readSheetHolder = new CsvReadSheetHolder(readSheet, readWorkbookHolder);
+ break;
+ default:
+ break;
+ }
+ currentReadHolder = readSheetHolder;
+ if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) {
+ throw new ExcelAnalysisException("Cannot read sheet repeatedly.");
+ }
+ readWorkbookHolder.getHasReadSheet().add(readSheetHolder.getSheetNo());
+ if (log.isDebugEnabled()) {
+ log.debug("Began to read:{}", readSheetHolder);
+ }
+ }
+
+ @Override
+ public ReadWorkbookHolder readWorkbookHolder() {
+ return readWorkbookHolder;
+ }
+
+ @Override
+ public ReadSheetHolder readSheetHolder() {
+ return readSheetHolder;
+ }
+
+ @Override
+ public ReadRowHolder readRowHolder() {
+ return readRowHolder;
+ }
+
+ @Override
+ public void readRowHolder(ReadRowHolder readRowHolder) {
+ this.readRowHolder = readRowHolder;
+ }
+
+ @Override
+ public ReadHolder currentReadHolder() {
+ return currentReadHolder;
+ }
+
+ @Override
+ public Object getCustom() {
+ return readWorkbookHolder.getCustomObject();
+ }
+
+ @Override
+ public AnalysisEventProcessor analysisEventProcessor() {
+ return analysisEventProcessor;
+ }
+
+ @Override
+ public List readSheetList() {
+ return null;
+ }
+
+ @Override
+ public void readSheetList(List readSheetList) {
+
+ }
+
+ @Override
+ public ExcelTypeEnum getExcelType() {
+ return readWorkbookHolder.getExcelType();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return readWorkbookHolder.getInputStream();
+ }
+
+ @Override
+ public Integer getCurrentRowNum() {
+ return readRowHolder.getRowIndex();
+ }
+
+ @Override
+ public Integer getTotalCount() {
+ return readSheetHolder.getTotal();
+ }
+
+ @Override
+ public Object getCurrentRowAnalysisResult() {
+ return readRowHolder.getCurrentRowAnalysisResult();
+ }
+
+ @Override
+ public void interrupt() {
+ throw new ExcelAnalysisException("interrupt error");
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java
new file mode 100644
index 000000000..0a59e1d09
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java
@@ -0,0 +1,110 @@
+package com.alibaba.excel.context;
+
+import java.io.OutputStream;
+
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import com.alibaba.excel.enums.WriteTypeEnum;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.WriteTable;
+import com.alibaba.excel.write.metadata.holder.WriteHolder;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+
+/**
+ * Write context
+ *
+ * @author jipengfei
+ */
+public interface WriteContext {
+ /**
+ * If the current sheet already exists, select it; if not, create it
+ *
+ * @param writeSheet
+ * Current sheet
+ * @param writeType
+ */
+ void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType);
+
+ /**
+ * If the current table already exists, select it; if not, create it
+ *
+ * @param writeTable
+ */
+ void currentTable(WriteTable writeTable);
+
+ /**
+ * All information about the workbook you are currently working on
+ *
+ * @return
+ */
+ WriteWorkbookHolder writeWorkbookHolder();
+
+ /**
+ * All information about the sheet you are currently working on
+ *
+ * @return
+ */
+ WriteSheetHolder writeSheetHolder();
+
+ /**
+ * All information about the table you are currently working on
+ *
+ * @return
+ */
+ WriteTableHolder writeTableHolder();
+
+ /**
+ * Configuration of currently operated cell. May be 'writeSheetHolder' or 'writeTableHolder' or
+ * 'writeWorkbookHolder'
+ *
+ * @return
+ */
+ WriteHolder currentWriteHolder();
+
+ /**
+ * close
+ *
+ * @param onException
+ */
+ void finish(boolean onException);
+
+ /**
+ * Current sheet
+ *
+ * @return
+ * @deprecated please us e{@link #writeSheetHolder()}
+ */
+ @Deprecated
+ Sheet getCurrentSheet();
+
+ /**
+ * Need head
+ *
+ * @return
+ * @deprecated please us e{@link #writeSheetHolder()}
+ */
+ @Deprecated
+ boolean needHead();
+
+ /**
+ * Get outputStream
+ *
+ * @return
+ * @deprecated please us e{@link #writeWorkbookHolder()} ()}
+ */
+ @Deprecated
+ OutputStream getOutputStream();
+
+ /**
+ * Get workbook
+ *
+ * @return
+ * @deprecated please us e{@link #writeWorkbookHolder()} ()}
+ */
+ @Deprecated
+ Workbook getWorkbook();
+
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java
new file mode 100644
index 000000000..9fff84491
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java
@@ -0,0 +1,524 @@
+package com.alibaba.excel.context;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.UUID;
+
+import com.alibaba.excel.enums.WriteTypeEnum;
+import com.alibaba.excel.exception.ExcelGenerateException;
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.util.ClassUtils;
+import com.alibaba.excel.util.DateUtils;
+import com.alibaba.excel.util.FileUtils;
+import com.alibaba.excel.util.ListUtils;
+import com.alibaba.excel.util.NumberDataFormatterUtils;
+import com.alibaba.excel.util.StringUtils;
+import com.alibaba.excel.util.WorkBookUtil;
+import com.alibaba.excel.util.WriteHandlerUtils;
+import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
+import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
+import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext;
+import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.WriteTable;
+import com.alibaba.excel.write.metadata.WriteWorkbook;
+import com.alibaba.excel.write.metadata.holder.WriteHolder;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import com.alibaba.excel.write.property.ExcelWriteHeadProperty;
+
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A context is the main anchorage point of a excel writer.
+ *
+ * @author jipengfei
+ */
+public class WriteContextImpl implements WriteContext {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(WriteContextImpl.class);
+ private static final String NO_SHEETS = "no sheets";
+
+ /**
+ * The Workbook currently written
+ */
+ private WriteWorkbookHolder writeWorkbookHolder;
+ /**
+ * Current sheet holder
+ */
+ private WriteSheetHolder writeSheetHolder;
+ /**
+ * The table currently written
+ */
+ private WriteTableHolder writeTableHolder;
+ /**
+ * Configuration of currently operated cell
+ */
+ private WriteHolder currentWriteHolder;
+ /**
+ * Prevent multiple shutdowns
+ */
+ private boolean finished = false;
+
+ public WriteContextImpl(WriteWorkbook writeWorkbook) {
+ if (writeWorkbook == null) {
+ throw new IllegalArgumentException("Workbook argument cannot be null");
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Begin to Initialization 'WriteContextImpl'");
+ }
+ initCurrentWorkbookHolder(writeWorkbook);
+
+ WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext(
+ this);
+ WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext);
+ try {
+ WorkBookUtil.createWorkBook(writeWorkbookHolder);
+ } catch (Exception e) {
+ throw new ExcelGenerateException("Create workbook failure", e);
+ }
+ WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Initialization 'WriteContextImpl' complete");
+ }
+ }
+
+ private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) {
+ writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook);
+ currentWriteHolder = writeWorkbookHolder;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("CurrentConfiguration is writeWorkbookHolder");
+ }
+ }
+
+ /**
+ * @param writeSheet
+ */
+ @Override
+ public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) {
+ if (writeSheet == null) {
+ throw new IllegalArgumentException("Sheet argument cannot be null");
+ }
+ if (selectSheetFromCache(writeSheet)) {
+ return;
+ }
+
+ initCurrentSheetHolder(writeSheet);
+
+ // Workbook handler need to supplementary execution
+ WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext(
+ this);
+ WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext, true);
+ WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext, true);
+
+ // Initialization current sheet
+ initSheet(writeType);
+ }
+
+ private boolean selectSheetFromCache(WriteSheet writeSheet) {
+ writeSheetHolder = null;
+ Integer sheetNo = writeSheet.getSheetNo();
+ if (sheetNo == null && StringUtils.isEmpty(writeSheet.getSheetName())) {
+ sheetNo = 0;
+ }
+ if (sheetNo != null) {
+ writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().get(sheetNo);
+ }
+ if (writeSheetHolder == null && !StringUtils.isEmpty(writeSheet.getSheetName())) {
+ writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetNameMap().get(writeSheet.getSheetName());
+ }
+ if (writeSheetHolder == null) {
+ return false;
+ }
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Sheet:{},{} is already existed", writeSheet.getSheetNo(), writeSheet.getSheetName());
+ }
+ writeSheetHolder.setNewInitialization(Boolean.FALSE);
+ writeTableHolder = null;
+ currentWriteHolder = writeSheetHolder;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("CurrentConfiguration is writeSheetHolder");
+ }
+ return true;
+ }
+
+ private void initCurrentSheetHolder(WriteSheet writeSheet) {
+ writeSheetHolder = new WriteSheetHolder(writeSheet, writeWorkbookHolder);
+ writeTableHolder = null;
+ currentWriteHolder = writeSheetHolder;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("CurrentConfiguration is writeSheetHolder");
+ }
+ }
+
+ private void initSheet(WriteTypeEnum writeType) {
+ SheetWriteHandlerContext sheetWriteHandlerContext = WriteHandlerUtils.createSheetWriteHandlerContext(this);
+ WriteHandlerUtils.beforeSheetCreate(sheetWriteHandlerContext);
+ Sheet currentSheet;
+ try {
+ if (writeSheetHolder.getSheetNo() != null) {
+ // When the add default sort order of appearance
+ if (WriteTypeEnum.ADD.equals(writeType) && writeWorkbookHolder.getTempTemplateInputStream() == null) {
+ currentSheet = createSheet();
+ } else {
+ currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo());
+ writeSheetHolder
+ .setCachedSheet(
+ writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo()));
+ }
+ } else {
+ // sheet name must not null
+ currentSheet = writeWorkbookHolder.getWorkbook().getSheet(writeSheetHolder.getSheetName());
+ writeSheetHolder
+ .setCachedSheet(writeWorkbookHolder.getCachedWorkbook().getSheet(writeSheetHolder.getSheetName()));
+ }
+ } catch (IllegalArgumentException e) {
+ if (e.getMessage() != null && e.getMessage().contains(NO_SHEETS)) {
+ currentSheet = createSheet();
+ } else {
+ throw e;
+ }
+ }
+ if (currentSheet == null) {
+ currentSheet = createSheet();
+ }
+ writeSheetHolder.setSheet(currentSheet);
+ WriteHandlerUtils.afterSheetCreate(sheetWriteHandlerContext);
+ if (WriteTypeEnum.ADD.equals(writeType)) {
+ // Initialization head
+ initHead(writeSheetHolder.excelWriteHeadProperty());
+ }
+ writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().put(writeSheetHolder.getSheetNo(), writeSheetHolder);
+ writeWorkbookHolder.getHasBeenInitializedSheetNameMap().put(writeSheetHolder.getSheetName(), writeSheetHolder);
+ }
+
+ private Sheet createSheet() {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo());
+ }
+ if (StringUtils.isEmpty(writeSheetHolder.getSheetName())) {
+ writeSheetHolder.setSheetName(writeSheetHolder.getSheetNo().toString());
+ }
+ Sheet currentSheet =
+ WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(), writeSheetHolder.getSheetName());
+ writeSheetHolder.setCachedSheet(currentSheet);
+ return currentSheet;
+ }
+
+ public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) {
+ if (!currentWriteHolder.needHead() || !currentWriteHolder.excelWriteHeadProperty().hasHead()) {
+ return;
+ }
+ int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
+ newRowIndex += currentWriteHolder.relativeHeadRowIndex();
+ // Combined head
+ if (currentWriteHolder.automaticMergeHead()) {
+ addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex);
+ }
+ for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber()
+ + newRowIndex; i++, relativeRowIndex++) {
+
+ RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(this,
+ newRowIndex, relativeRowIndex, Boolean.TRUE);
+ WriteHandlerUtils.beforeRowCreate(rowWriteHandlerContext);
+
+ Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i);
+ rowWriteHandlerContext.setRow(row);
+
+ WriteHandlerUtils.afterRowCreate(rowWriteHandlerContext);
+ addOneRowOfHeadDataToExcel(row, i, excelWriteHeadProperty.getHeadMap(), relativeRowIndex);
+ WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext);
+ }
+ }
+
+ private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) {
+ for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) {
+ writeSheetHolder.getSheet()
+ .addMergedRegionUnsafe(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex,
+ cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol()));
+ }
+ }
+
+ private void addOneRowOfHeadDataToExcel(Row row, Integer rowIndex, Map headMap,
+ int relativeRowIndex) {
+ for (Map.Entry entry : headMap.entrySet()) {
+ Head head = entry.getValue();
+ int columnIndex = entry.getKey();
+ ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null,
+ currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName(), currentWriteHolder);
+
+ CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(this, row,
+ rowIndex, head, columnIndex, relativeRowIndex, Boolean.TRUE, excelContentProperty);
+ WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext);
+
+ Cell cell = row.createCell(columnIndex);
+ cellWriteHandlerContext.setCell(cell);
+
+ WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext);
+
+ WriteCellData writeCellData = new WriteCellData<>(head.getHeadNameList().get(relativeRowIndex));
+ cell.setCellValue(writeCellData.getStringValue());
+ cellWriteHandlerContext.setCellDataList(ListUtils.newArrayList(writeCellData));
+ cellWriteHandlerContext.setFirstCellData(writeCellData);
+
+ WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext);
+ }
+ }
+
+ @Override
+ public void currentTable(WriteTable writeTable) {
+ if (writeTable == null) {
+ return;
+ }
+ if (writeTable.getTableNo() == null || writeTable.getTableNo() <= 0) {
+ writeTable.setTableNo(0);
+ }
+ if (writeSheetHolder.getHasBeenInitializedTable().containsKey(writeTable.getTableNo())) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Table:{} is already existed", writeTable.getTableNo());
+ }
+ writeTableHolder = writeSheetHolder.getHasBeenInitializedTable().get(writeTable.getTableNo());
+ writeTableHolder.setNewInitialization(Boolean.FALSE);
+ currentWriteHolder = writeTableHolder;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("CurrentConfiguration is writeTableHolder");
+ }
+ return;
+ }
+
+ initCurrentTableHolder(writeTable);
+
+ // Workbook and sheet handler need to supplementary execution
+ WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext(
+ this);
+ WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext, true);
+ WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext, true);
+
+ SheetWriteHandlerContext sheetWriteHandlerContext = WriteHandlerUtils.createSheetWriteHandlerContext(this);
+ WriteHandlerUtils.beforeSheetCreate(sheetWriteHandlerContext, true);
+ WriteHandlerUtils.afterSheetCreate(sheetWriteHandlerContext, true);
+
+ initHead(writeTableHolder.excelWriteHeadProperty());
+ }
+
+ private void initCurrentTableHolder(WriteTable writeTable) {
+ writeTableHolder = new WriteTableHolder(writeTable, writeSheetHolder);
+ writeSheetHolder.getHasBeenInitializedTable().put(writeTable.getTableNo(), writeTableHolder);
+ currentWriteHolder = writeTableHolder;
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("CurrentConfiguration is writeTableHolder");
+ }
+ }
+
+ @Override
+ public WriteWorkbookHolder writeWorkbookHolder() {
+ return writeWorkbookHolder;
+ }
+
+ @Override
+ public WriteSheetHolder writeSheetHolder() {
+ return writeSheetHolder;
+ }
+
+ @Override
+ public WriteTableHolder writeTableHolder() {
+ return writeTableHolder;
+ }
+
+ @Override
+ public WriteHolder currentWriteHolder() {
+ return currentWriteHolder;
+ }
+
+ @Override
+ public void finish(boolean onException) {
+ if (finished) {
+ return;
+ }
+ finished = true;
+ WriteHandlerUtils.afterWorkbookDispose(writeWorkbookHolder.getWorkbookWriteHandlerContext());
+ if (writeWorkbookHolder == null) {
+ return;
+ }
+ Throwable throwable = null;
+ boolean isOutputStreamEncrypt = false;
+ // Determine if you need to write excel
+ boolean writeExcel = !onException;
+ if (writeWorkbookHolder.getWriteExcelOnException()) {
+ writeExcel = Boolean.TRUE;
+ }
+ // No data is written if an exception is thrown
+ if (writeExcel) {
+ try {
+ isOutputStreamEncrypt = doOutputStreamEncrypt07();
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ }
+ if (!isOutputStreamEncrypt) {
+ try {
+ if (writeExcel) {
+ writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
+ }
+ writeWorkbookHolder.getWorkbook().close();
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ }
+ try {
+ Workbook workbook = writeWorkbookHolder.getWorkbook();
+ if (workbook instanceof SXSSFWorkbook) {
+ ((SXSSFWorkbook)workbook).dispose();
+ }
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ try {
+ if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) {
+ writeWorkbookHolder.getOutputStream().close();
+ }
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ if (writeExcel && !isOutputStreamEncrypt) {
+ try {
+ doFileEncrypt07();
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ }
+ try {
+ if (writeWorkbookHolder.getTempTemplateInputStream() != null) {
+ writeWorkbookHolder.getTempTemplateInputStream().close();
+ }
+ } 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();
+ ClassUtils.removeThreadLocalCache();
+ }
+
+ @Override
+ public Sheet getCurrentSheet() {
+ return writeSheetHolder.getSheet();
+ }
+
+ @Override
+ public boolean needHead() {
+ return writeSheetHolder.needHead();
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return writeWorkbookHolder.getOutputStream();
+ }
+
+ @Override
+ public Workbook getWorkbook() {
+ return writeWorkbookHolder.getWorkbook();
+ }
+
+ private void clearEncrypt03() {
+ if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
+ || !ExcelTypeEnum.XLS.equals(writeWorkbookHolder.getExcelType())) {
+ return;
+ }
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+ }
+
+ /**
+ * To encrypt
+ */
+ private boolean doOutputStreamEncrypt07() throws Exception {
+ if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
+ || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
+ return false;
+ }
+ if (writeWorkbookHolder.getFile() != null) {
+ return false;
+ }
+ File tempXlsx = FileUtils.createTmpFile(UUID.randomUUID() + ".xlsx");
+ FileOutputStream tempFileOutputStream = new FileOutputStream(tempXlsx);
+ try {
+ writeWorkbookHolder.getWorkbook().write(tempFileOutputStream);
+ } finally {
+ try {
+ writeWorkbookHolder.getWorkbook().close();
+ tempFileOutputStream.close();
+ } catch (Exception e) {
+ if (!tempXlsx.delete()) {
+ throw new ExcelGenerateException("Can not delete temp File!");
+ }
+ throw e;
+ }
+ }
+ try (POIFSFileSystem fileSystem = openFileSystemAndEncrypt(tempXlsx)) {
+ fileSystem.writeFilesystem(writeWorkbookHolder.getOutputStream());
+ } finally {
+ if (!tempXlsx.delete()) {
+ throw new ExcelGenerateException("Can not delete temp File!");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * To encrypt
+ */
+ private void doFileEncrypt07() throws Exception {
+ if (StringUtils.isEmpty(writeWorkbookHolder.getPassword())
+ || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
+ return;
+ }
+ if (writeWorkbookHolder.getFile() == null) {
+ return;
+ }
+ try (POIFSFileSystem fileSystem = openFileSystemAndEncrypt(writeWorkbookHolder.getFile());
+ FileOutputStream fileOutputStream = new FileOutputStream(writeWorkbookHolder.getFile())) {
+ fileSystem.writeFilesystem(fileOutputStream);
+ }
+ }
+
+ private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception {
+ POIFSFileSystem fileSystem = new POIFSFileSystem();
+ Encryptor encryptor = new EncryptionInfo(EncryptionMode.standard).getEncryptor();
+ encryptor.confirmPassword(writeWorkbookHolder.getPassword());
+ try (OPCPackage opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE);
+ OutputStream outputStream = encryptor.getDataStream(fileSystem)) {
+ opcPackage.save(outputStream);
+ }
+ return fileSystem;
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java
new file mode 100644
index 000000000..00bcd54ae
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java
@@ -0,0 +1,26 @@
+package com.alibaba.excel.context.csv;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.read.metadata.holder.csv.CsvReadSheetHolder;
+import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
+
+/**
+ * A context is the main anchorage point of a ls xls reader.
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface CsvReadContext extends AnalysisContext {
+ /**
+ * All information about the workbook you are currently working on.
+ *
+ * @return Current workbook holder
+ */
+ CsvReadWorkbookHolder csvReadWorkbookHolder();
+
+ /**
+ * All information about the sheet you are currently working on.
+ *
+ * @return Current sheet holder
+ */
+ CsvReadSheetHolder csvReadSheetHolder();
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java
new file mode 100644
index 000000000..d8653a88b
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java
@@ -0,0 +1,29 @@
+package com.alibaba.excel.context.csv;
+
+import com.alibaba.excel.context.AnalysisContextImpl;
+import com.alibaba.excel.read.metadata.ReadWorkbook;
+import com.alibaba.excel.read.metadata.holder.csv.CsvReadSheetHolder;
+import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
+import com.alibaba.excel.support.ExcelTypeEnum;
+
+/**
+ * A context is the main anchorage point of a ls xls reader.
+ *
+ * @author Jiaju Zhuang
+ */
+public class DefaultCsvReadContext extends AnalysisContextImpl implements CsvReadContext {
+
+ public DefaultCsvReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) {
+ super(readWorkbook, actualExcelType);
+ }
+
+ @Override
+ public CsvReadWorkbookHolder csvReadWorkbookHolder() {
+ return (CsvReadWorkbookHolder)readWorkbookHolder();
+ }
+
+ @Override
+ public CsvReadSheetHolder csvReadSheetHolder() {
+ return (CsvReadSheetHolder)readSheetHolder();
+ }
+}
diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java
new file mode 100644
index 000000000..698bf6a40
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java
new file mode 100644
index 000000000..4d1bcdb29
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java
new file mode 100644
index 000000000..5c9d638d4
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java
new file mode 100644
index 000000000..58d8123b2
--- /dev/null
+++ b/easyexcel-core/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/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java
new file mode 100644
index 000000000..5981e7acf
--- /dev/null
+++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java
@@ -0,0 +1,9 @@
+package com.alibaba.excel.converters;
+
+/**
+ * An empty converter.It's automatically converted by type.
+ *
+ * @author Jiaju Zhuang
+ */
+public class AutoConverter implements Converter