+ * 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-plus-core/src/main/java/ai/chat2db/excel/analysis/v07/handlers/sax/XlsxRowHandler.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/analysis/v07/handlers/sax/XlsxRowHandler.java
new file mode 100644
index 0000000..c7cbd4d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/analysis/v07/handlers/sax/XlsxRowHandler.java
@@ -0,0 +1,103 @@
+package ai.chat2db.excel.analysis.v07.handlers.sax;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ai.chat2db.excel.constant.ExcelXmlConstants;
+import ai.chat2db.excel.analysis.v07.handlers.CellFormulaTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.CellInlineStringValueTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.CellTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.CellValueTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.CountTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.HyperlinkTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.MergeCellTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.RowTagHandler;
+import ai.chat2db.excel.analysis.v07.handlers.XlsxTagHandler;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnore.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnore.java
new file mode 100644
index 0000000..00180e7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnore.java
@@ -0,0 +1,17 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnoreUnannotated.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnoreUnannotated.java
new file mode 100644
index 0000000..fd519a1
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelIgnoreUnannotated.java
@@ -0,0 +1,18 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelProperty.java
new file mode 100644
index 0000000..0b69916
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/ExcelProperty.java
@@ -0,0 +1,69 @@
+package ai.chat2db.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 ai.chat2db.excel.converters.AutoConverter;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.annotation.format.DateTimeFormat;
+
+/**
+ * @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 DateTimeFormat}
+ */
+ @Deprecated
+ String format() default "";
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/format/DateTimeFormat.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/format/DateTimeFormat.java
new file mode 100644
index 0000000..0b87b49
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/format/DateTimeFormat.java
@@ -0,0 +1,40 @@
+package ai.chat2db.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 ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/format/NumberFormat.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/format/NumberFormat.java
new file mode 100644
index 0000000..ca7675f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/format/NumberFormat.java
@@ -0,0 +1,39 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ColumnWidth.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ColumnWidth.java
new file mode 100644
index 0000000..b228a21
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ColumnWidth.java
@@ -0,0 +1,27 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentFontStyle.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentFontStyle.java
new file mode 100644
index 0000000..ba868b5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentFontStyle.java
@@ -0,0 +1,91 @@
+package ai.chat2db.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 ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentLoopMerge.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentLoopMerge.java
new file mode 100644
index 0000000..f209fdf
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentLoopMerge.java
@@ -0,0 +1,31 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentRowHeight.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentRowHeight.java
new file mode 100644
index 0000000..0ab51d0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentRowHeight.java
@@ -0,0 +1,27 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentStyle.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentStyle.java
new file mode 100644
index 0000000..95121b0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/ContentStyle.java
@@ -0,0 +1,162 @@
+package ai.chat2db.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 ai.chat2db.excel.enums.BooleanEnum;
+import ai.chat2db.excel.enums.poi.BorderStyleEnum;
+import ai.chat2db.excel.enums.poi.FillPatternTypeEnum;
+import ai.chat2db.excel.enums.poi.HorizontalAlignmentEnum;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadFontStyle.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadFontStyle.java
new file mode 100644
index 0000000..c415d2a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadFontStyle.java
@@ -0,0 +1,91 @@
+package ai.chat2db.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 ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadRowHeight.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadRowHeight.java
new file mode 100644
index 0000000..c0ae6ee
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadRowHeight.java
@@ -0,0 +1,26 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadStyle.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadStyle.java
new file mode 100644
index 0000000..db2e774
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/HeadStyle.java
@@ -0,0 +1,156 @@
+package ai.chat2db.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 ai.chat2db.excel.enums.BooleanEnum;
+import ai.chat2db.excel.enums.poi.BorderStyleEnum;
+import ai.chat2db.excel.enums.poi.FillPatternTypeEnum;
+import ai.chat2db.excel.enums.poi.HorizontalAlignmentEnum;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/OnceAbsoluteMerge.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/OnceAbsoluteMerge.java
new file mode 100644
index 0000000..3d5bdf8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/annotation/write/style/OnceAbsoluteMerge.java
@@ -0,0 +1,45 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/cache/Ehcache.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/Ehcache.java
new file mode 100644
index 0000000..ccac093
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/Ehcache.java
@@ -0,0 +1,163 @@
+package ai.chat2db.excel.cache;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.util.FileUtils;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/cache/MapCache.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/MapCache.java
new file mode 100644
index 0000000..3ab8830
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/MapCache.java
@@ -0,0 +1,38 @@
+package ai.chat2db.excel.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/cache/ReadCache.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/ReadCache.java
new file mode 100644
index 0000000..768765d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/ReadCache.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.cache;
+
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/cache/XlsCache.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/XlsCache.java
new file mode 100644
index 0000000..fcebae2
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/XlsCache.java
@@ -0,0 +1,37 @@
+package ai.chat2db.excel.cache;
+
+import org.apache.poi.hssf.record.SSTRecord;
+
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/cache/selector/EternalReadCacheSelector.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/EternalReadCacheSelector.java
new file mode 100644
index 0000000..0879296
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/EternalReadCacheSelector.java
@@ -0,0 +1,22 @@
+package ai.chat2db.excel.cache.selector;
+
+import ai.chat2db.excel.cache.ReadCache;
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+/**
+ * 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-plus-core/src/main/java/ai/chat2db/excel/cache/selector/ReadCacheSelector.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/ReadCacheSelector.java
new file mode 100644
index 0000000..6d799a1
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/ReadCacheSelector.java
@@ -0,0 +1,20 @@
+package ai.chat2db.excel.cache.selector;
+
+import ai.chat2db.excel.cache.ReadCache;
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+/**
+ * Select the cache
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface ReadCacheSelector {
+
+ /**
+ * Select a cache
+ *
+ * @param sharedStringsTablePackagePart
+ * @return
+ */
+ ReadCache readCache(PackagePart sharedStringsTablePackagePart);
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/SimpleReadCacheSelector.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/SimpleReadCacheSelector.java
new file mode 100644
index 0000000..ab1a8bb
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/cache/selector/SimpleReadCacheSelector.java
@@ -0,0 +1,110 @@
+package ai.chat2db.excel.cache.selector;
+
+import java.io.IOException;
+
+import ai.chat2db.excel.cache.Ehcache;
+import ai.chat2db.excel.cache.MapCache;
+import ai.chat2db.excel.cache.ReadCache;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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-plus-core/src/main/java/ai/chat2db/excel/constant/BuiltinFormats.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/BuiltinFormats.java
new file mode 100644
index 0000000..3b67c44
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/BuiltinFormats.java
@@ -0,0 +1,530 @@
+package ai.chat2db.excel.constant;
+
+import java.util.Locale;
+import java.util.Map;
+
+import ai.chat2db.excel.util.MapUtils;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/constant/EasyExcelConstants.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/EasyExcelConstants.java
new file mode 100644
index 0000000..bf03a03
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/EasyExcelConstants.java
@@ -0,0 +1,19 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/constant/ExcelXmlConstants.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/ExcelXmlConstants.java
new file mode 100644
index 0000000..f7479cd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/ExcelXmlConstants.java
@@ -0,0 +1,99 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/constant/OrderConstant.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/OrderConstant.java
new file mode 100644
index 0000000..b26c15c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/constant/OrderConstant.java
@@ -0,0 +1,34 @@
+package ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContext.java
new file mode 100644
index 0000000..4a1a4dc
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContext.java
@@ -0,0 +1,147 @@
+package ai.chat2db.excel.context;
+
+import java.io.InputStream;
+import java.util.List;
+
+import ai.chat2db.excel.event.AnalysisEventListener;
+import ai.chat2db.excel.read.metadata.ReadSheet;
+import ai.chat2db.excel.read.metadata.holder.ReadHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadRowHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadWorkbookHolder;
+import ai.chat2db.excel.read.processor.AnalysisEventProcessor;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContextImpl.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContextImpl.java
new file mode 100644
index 0000000..311ba1a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/AnalysisContextImpl.java
@@ -0,0 +1,174 @@
+package ai.chat2db.excel.context;
+
+import java.io.InputStream;
+import java.util.List;
+
+import ai.chat2db.excel.exception.ExcelAnalysisException;
+import ai.chat2db.excel.read.metadata.ReadSheet;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.read.metadata.holder.ReadHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadRowHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.ReadWorkbookHolder;
+import ai.chat2db.excel.read.metadata.holder.csv.CsvReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
+import ai.chat2db.excel.read.metadata.holder.xls.XlsReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.xls.XlsReadWorkbookHolder;
+import ai.chat2db.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder;
+import ai.chat2db.excel.read.processor.AnalysisEventProcessor;
+import ai.chat2db.excel.read.processor.DefaultAnalysisEventProcessor;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/WriteContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/WriteContext.java
new file mode 100644
index 0000000..daf2ec0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/WriteContext.java
@@ -0,0 +1,110 @@
+package ai.chat2db.excel.context;
+
+import java.io.OutputStream;
+
+import ai.chat2db.excel.enums.WriteTypeEnum;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import ai.chat2db.excel.write.metadata.WriteSheet;
+import ai.chat2db.excel.write.metadata.WriteTable;
+import ai.chat2db.excel.write.metadata.holder.WriteHolder;
+import ai.chat2db.excel.write.metadata.holder.WriteSheetHolder;
+import ai.chat2db.excel.write.metadata.holder.WriteTableHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/WriteContextImpl.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/WriteContextImpl.java
new file mode 100644
index 0000000..9aff0e6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/WriteContextImpl.java
@@ -0,0 +1,525 @@
+package ai.chat2db.excel.context;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.UUID;
+
+import ai.chat2db.excel.enums.WriteTypeEnum;
+import ai.chat2db.excel.exception.ExcelGenerateException;
+import ai.chat2db.excel.metadata.CellRange;
+import ai.chat2db.excel.metadata.Head;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+import ai.chat2db.excel.write.handler.context.CellWriteHandlerContext;
+import ai.chat2db.excel.write.handler.context.RowWriteHandlerContext;
+import ai.chat2db.excel.write.handler.context.SheetWriteHandlerContext;
+import ai.chat2db.excel.write.handler.context.WorkbookWriteHandlerContext;
+import ai.chat2db.excel.support.ExcelTypeEnum;
+import ai.chat2db.excel.util.ClassUtils;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.FileUtils;
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.util.NumberDataFormatterUtils;
+import ai.chat2db.excel.util.StringUtils;
+import ai.chat2db.excel.util.WorkBookUtil;
+import ai.chat2db.excel.util.WriteHandlerUtils;
+import ai.chat2db.excel.write.metadata.WriteSheet;
+import ai.chat2db.excel.write.metadata.WriteTable;
+import ai.chat2db.excel.write.metadata.WriteWorkbook;
+import ai.chat2db.excel.write.metadata.holder.WriteHolder;
+import ai.chat2db.excel.write.metadata.holder.WriteSheetHolder;
+import ai.chat2db.excel.write.metadata.holder.WriteTableHolder;
+import ai.chat2db.excel.write.metadata.holder.WriteWorkbookHolder;
+import ai.chat2db.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 (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-plus-core/src/main/java/ai/chat2db/excel/context/csv/CsvReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/csv/CsvReadContext.java
new file mode 100644
index 0000000..0dbfcb7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/csv/CsvReadContext.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.context.csv;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.read.metadata.holder.csv.CsvReadSheetHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/csv/DefaultCsvReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/csv/DefaultCsvReadContext.java
new file mode 100644
index 0000000..fe63778
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/csv/DefaultCsvReadContext.java
@@ -0,0 +1,29 @@
+package ai.chat2db.excel.context.csv;
+
+import ai.chat2db.excel.context.AnalysisContextImpl;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.read.metadata.holder.csv.CsvReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.csv.CsvReadWorkbookHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/xls/DefaultXlsReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xls/DefaultXlsReadContext.java
new file mode 100644
index 0000000..d22c6a3
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xls/DefaultXlsReadContext.java
@@ -0,0 +1,30 @@
+package ai.chat2db.excel.context.xls;
+
+import ai.chat2db.excel.context.AnalysisContextImpl;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.read.metadata.holder.xls.XlsReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.xls.XlsReadWorkbookHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/xls/XlsReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xls/XlsReadContext.java
new file mode 100644
index 0000000..ead8069
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xls/XlsReadContext.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.context.xls;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.read.metadata.holder.xls.XlsReadSheetHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/DefaultXlsxReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/DefaultXlsxReadContext.java
new file mode 100644
index 0000000..cedf715
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/DefaultXlsxReadContext.java
@@ -0,0 +1,30 @@
+package ai.chat2db.excel.context.xlsx;
+
+import ai.chat2db.excel.context.AnalysisContextImpl;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder;
+import ai.chat2db.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/XlsxReadContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/XlsxReadContext.java
new file mode 100644
index 0000000..f1f4280
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/context/xlsx/XlsxReadContext.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.context.xlsx;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder;
+import ai.chat2db.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-plus-core/src/main/java/ai/chat2db/excel/converters/AutoConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/AutoConverter.java
new file mode 100644
index 0000000..bf20b1d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/AutoConverter.java
@@ -0,0 +1,9 @@
+package ai.chat2db.excel.converters;
+
+/**
+ * An empty converter.It's automatically converted by type.
+ *
+ * @author Jiaju Zhuang
+ */
+public class AutoConverter implements Converter {
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/Converter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/Converter.java
new file mode 100644
index 0000000..85bcbba
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/Converter.java
@@ -0,0 +1,86 @@
+package ai.chat2db.excel.converters;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Convert between Java objects and excel objects
+ *
+ * @param
+ * @author Dan Zheng
+ */
+public interface Converter {
+
+ /**
+ * Back to object types in Java
+ *
+ * @return Support for Java class
+ */
+ default Class> supportJavaTypeKey() {
+ throw new UnsupportedOperationException("The current operation is not supported by the current converter.");
+ }
+
+ /**
+ * Back to object enum in excel
+ *
+ * @return Support for {@link CellDataTypeEnum}
+ */
+ default CellDataTypeEnum supportExcelTypeKey() {
+ throw new UnsupportedOperationException("The current operation is not supported by the current converter.");
+ }
+
+ /**
+ * Convert excel objects to Java objects
+ *
+ * @param cellData Excel cell data.NotNull.
+ * @param contentProperty Content property.Nullable.
+ * @param globalConfiguration Global configuration.NotNull.
+ * @return Data to put into a Java object
+ * @throws Exception Exception.
+ */
+ default T convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws Exception {
+ throw new UnsupportedOperationException("The current operation is not supported by the current converter.");
+ }
+
+ /**
+ * Convert excel objects to Java objects
+ *
+ * @param context read converter context
+ * @return Data to put into a Java object
+ * @throws Exception Exception.
+ */
+ default T convertToJavaData(ReadConverterContext> context) throws Exception {
+ return convertToJavaData(context.getReadCellData(), context.getContentProperty(),
+ context.getAnalysisContext().currentReadHolder().globalConfiguration());
+ }
+
+ /**
+ * Convert Java objects to excel objects
+ *
+ * @param value Java Data.NotNull.
+ * @param contentProperty Content property.Nullable.
+ * @param globalConfiguration Global configuration.NotNull.
+ * @return Data to put into a Excel
+ * @throws Exception Exception.
+ */
+ default WriteCellData> convertToExcelData(T value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws Exception {
+ throw new UnsupportedOperationException("The current operation is not supported by the current converter.");
+ }
+
+ /**
+ * Convert Java objects to excel objects
+ *
+ * @param context write context
+ * @return Data to put into a Excel
+ * @throws Exception Exception.
+ */
+ default WriteCellData> convertToExcelData(WriteConverterContext context) throws Exception {
+ return convertToExcelData(context.getValue(), context.getContentProperty(),
+ context.getWriteContext().currentWriteHolder().globalConfiguration());
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ConverterKeyBuild.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ConverterKeyBuild.java
new file mode 100644
index 0000000..a2eb59d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ConverterKeyBuild.java
@@ -0,0 +1,53 @@
+package ai.chat2db.excel.converters;
+
+import java.util.Map;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.MapUtils;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Converter unique key.Consider that you can just use class as the key.
+ *
+ * @author Jiaju Zhuang
+ */
+public class ConverterKeyBuild {
+
+ private static final Map, Class>> BOXING_MAP = MapUtils.newHashMap();
+
+ static {
+ BOXING_MAP.put(int.class, Integer.class);
+ BOXING_MAP.put(byte.class, Byte.class);
+ BOXING_MAP.put(long.class, Long.class);
+ BOXING_MAP.put(double.class, Double.class);
+ BOXING_MAP.put(float.class, Float.class);
+ BOXING_MAP.put(char.class, Character.class);
+ BOXING_MAP.put(short.class, Short.class);
+ BOXING_MAP.put(boolean.class, Boolean.class);
+ }
+
+ public static ConverterKey buildKey(Class> clazz) {
+ return buildKey(clazz, null);
+ }
+
+ public static ConverterKey buildKey(Class> clazz, CellDataTypeEnum cellDataTypeEnum) {
+ Class> boxingClass = BOXING_MAP.get(clazz);
+ if (boxingClass != null) {
+ return new ConverterKey(boxingClass, cellDataTypeEnum);
+ }
+ return new ConverterKey(clazz, cellDataTypeEnum);
+ }
+
+ @Getter
+ @Setter
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ public static class ConverterKey {
+ private Class> clazz;
+ private CellDataTypeEnum cellDataTypeEnum;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/DefaultConverterLoader.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/DefaultConverterLoader.java
new file mode 100644
index 0000000..8335ac4
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/DefaultConverterLoader.java
@@ -0,0 +1,197 @@
+package ai.chat2db.excel.converters;
+
+import java.util.Map;
+
+import ai.chat2db.excel.util.MapUtils;
+import ai.chat2db.excel.converters.ConverterKeyBuild.ConverterKey;
+import ai.chat2db.excel.converters.bigdecimal.BigDecimalBooleanConverter;
+import ai.chat2db.excel.converters.bigdecimal.BigDecimalNumberConverter;
+import ai.chat2db.excel.converters.bigdecimal.BigDecimalStringConverter;
+import ai.chat2db.excel.converters.biginteger.BigIntegerBooleanConverter;
+import ai.chat2db.excel.converters.biginteger.BigIntegerNumberConverter;
+import ai.chat2db.excel.converters.biginteger.BigIntegerStringConverter;
+import ai.chat2db.excel.converters.booleanconverter.BooleanBooleanConverter;
+import ai.chat2db.excel.converters.booleanconverter.BooleanNumberConverter;
+import ai.chat2db.excel.converters.booleanconverter.BooleanStringConverter;
+import ai.chat2db.excel.converters.bytearray.BoxingByteArrayImageConverter;
+import ai.chat2db.excel.converters.bytearray.ByteArrayImageConverter;
+import ai.chat2db.excel.converters.byteconverter.ByteBooleanConverter;
+import ai.chat2db.excel.converters.byteconverter.ByteNumberConverter;
+import ai.chat2db.excel.converters.byteconverter.ByteStringConverter;
+import ai.chat2db.excel.converters.date.DateDateConverter;
+import ai.chat2db.excel.converters.date.DateNumberConverter;
+import ai.chat2db.excel.converters.date.DateStringConverter;
+import ai.chat2db.excel.converters.doubleconverter.DoubleBooleanConverter;
+import ai.chat2db.excel.converters.doubleconverter.DoubleNumberConverter;
+import ai.chat2db.excel.converters.doubleconverter.DoubleStringConverter;
+import ai.chat2db.excel.converters.file.FileImageConverter;
+import ai.chat2db.excel.converters.floatconverter.FloatBooleanConverter;
+import ai.chat2db.excel.converters.floatconverter.FloatNumberConverter;
+import ai.chat2db.excel.converters.floatconverter.FloatStringConverter;
+import ai.chat2db.excel.converters.inputstream.InputStreamImageConverter;
+import ai.chat2db.excel.converters.integer.IntegerBooleanConverter;
+import ai.chat2db.excel.converters.integer.IntegerNumberConverter;
+import ai.chat2db.excel.converters.integer.IntegerStringConverter;
+import ai.chat2db.excel.converters.localdate.LocalDateDateConverter;
+import ai.chat2db.excel.converters.localdate.LocalDateNumberConverter;
+import ai.chat2db.excel.converters.localdate.LocalDateStringConverter;
+import ai.chat2db.excel.converters.localdatetime.LocalDateTimeNumberConverter;
+import ai.chat2db.excel.converters.localdatetime.LocalDateTimeDateConverter;
+import ai.chat2db.excel.converters.localdatetime.LocalDateTimeStringConverter;
+import ai.chat2db.excel.converters.longconverter.LongBooleanConverter;
+import ai.chat2db.excel.converters.longconverter.LongNumberConverter;
+import ai.chat2db.excel.converters.longconverter.LongStringConverter;
+import ai.chat2db.excel.converters.shortconverter.ShortBooleanConverter;
+import ai.chat2db.excel.converters.shortconverter.ShortNumberConverter;
+import ai.chat2db.excel.converters.shortconverter.ShortStringConverter;
+import ai.chat2db.excel.converters.string.StringBooleanConverter;
+import ai.chat2db.excel.converters.string.StringErrorConverter;
+import ai.chat2db.excel.converters.string.StringNumberConverter;
+import ai.chat2db.excel.converters.string.StringStringConverter;
+import ai.chat2db.excel.converters.url.UrlImageConverter;
+
+/**
+ * Load default handler
+ *
+ * @author Jiaju Zhuang
+ */
+public class DefaultConverterLoader {
+ private static Map> defaultWriteConverter;
+ private static Map> allConverter;
+
+ static {
+ initDefaultWriteConverter();
+ initAllConverter();
+ }
+
+ private static void initAllConverter() {
+ allConverter = MapUtils.newHashMapWithExpectedSize(40);
+ putAllConverter(new BigDecimalBooleanConverter());
+ putAllConverter(new BigDecimalNumberConverter());
+ putAllConverter(new BigDecimalStringConverter());
+
+ putAllConverter(new BigIntegerBooleanConverter());
+ putAllConverter(new BigIntegerNumberConverter());
+ putAllConverter(new BigIntegerStringConverter());
+
+ putAllConverter(new BooleanBooleanConverter());
+ putAllConverter(new BooleanNumberConverter());
+ putAllConverter(new BooleanStringConverter());
+
+ putAllConverter(new ByteBooleanConverter());
+ putAllConverter(new ByteNumberConverter());
+ putAllConverter(new ByteStringConverter());
+
+ putAllConverter(new DateNumberConverter());
+ putAllConverter(new DateStringConverter());
+
+ putAllConverter(new LocalDateNumberConverter());
+ putAllConverter(new LocalDateStringConverter());
+
+ putAllConverter(new LocalDateTimeNumberConverter());
+ putAllConverter(new LocalDateTimeStringConverter());
+
+ putAllConverter(new DoubleBooleanConverter());
+ putAllConverter(new DoubleNumberConverter());
+ putAllConverter(new DoubleStringConverter());
+
+ putAllConverter(new FloatBooleanConverter());
+ putAllConverter(new FloatNumberConverter());
+ putAllConverter(new FloatStringConverter());
+
+ putAllConverter(new IntegerBooleanConverter());
+ putAllConverter(new IntegerNumberConverter());
+ putAllConverter(new IntegerStringConverter());
+
+ putAllConverter(new LongBooleanConverter());
+ putAllConverter(new LongNumberConverter());
+ putAllConverter(new LongStringConverter());
+
+ putAllConverter(new ShortBooleanConverter());
+ putAllConverter(new ShortNumberConverter());
+ putAllConverter(new ShortStringConverter());
+
+ putAllConverter(new StringBooleanConverter());
+ putAllConverter(new StringNumberConverter());
+ putAllConverter(new StringStringConverter());
+ putAllConverter(new StringErrorConverter());
+ }
+
+ private static void initDefaultWriteConverter() {
+ defaultWriteConverter = MapUtils.newHashMapWithExpectedSize(40);
+ putWriteConverter(new BigDecimalNumberConverter());
+ putWriteConverter(new BigIntegerNumberConverter());
+ putWriteConverter(new BooleanBooleanConverter());
+ putWriteConverter(new ByteNumberConverter());
+ putWriteConverter(new DateDateConverter());
+ putWriteConverter(new LocalDateTimeDateConverter());
+ putWriteConverter(new LocalDateDateConverter());
+ putWriteConverter(new DoubleNumberConverter());
+ putWriteConverter(new FloatNumberConverter());
+ putWriteConverter(new IntegerNumberConverter());
+ putWriteConverter(new LongNumberConverter());
+ putWriteConverter(new ShortNumberConverter());
+ putWriteConverter(new StringStringConverter());
+ putWriteConverter(new FileImageConverter());
+ putWriteConverter(new InputStreamImageConverter());
+ putWriteConverter(new ByteArrayImageConverter());
+ putWriteConverter(new BoxingByteArrayImageConverter());
+ putWriteConverter(new UrlImageConverter());
+
+ // In some cases, it must be converted to string
+ putWriteStringConverter(new BigDecimalStringConverter());
+ putWriteStringConverter(new BigIntegerStringConverter());
+ putWriteStringConverter(new BooleanStringConverter());
+ putWriteStringConverter(new ByteStringConverter());
+ putWriteStringConverter(new DateStringConverter());
+ putWriteStringConverter(new LocalDateStringConverter());
+ putWriteStringConverter(new LocalDateTimeStringConverter());
+ putWriteStringConverter(new DoubleStringConverter());
+ putWriteStringConverter(new FloatStringConverter());
+ putWriteStringConverter(new IntegerStringConverter());
+ putWriteStringConverter(new LongStringConverter());
+ putWriteStringConverter(new ShortStringConverter());
+ putWriteStringConverter(new StringStringConverter());
+ }
+
+ /**
+ * Load default write converter
+ *
+ * @return
+ */
+ public static Map> loadDefaultWriteConverter() {
+ return defaultWriteConverter;
+ }
+
+ private static void putWriteConverter(Converter> converter) {
+ defaultWriteConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
+ }
+
+ private static void putWriteStringConverter(Converter> converter) {
+ defaultWriteConverter.put(
+ ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), converter);
+ }
+
+ /**
+ * Load default read converter
+ *
+ * @return
+ */
+ public static Map> loadDefaultReadConverter() {
+ return loadAllConverter();
+ }
+
+ /**
+ * Load all converter
+ *
+ * @return
+ */
+ public static Map> loadAllConverter() {
+ return allConverter;
+ }
+
+ private static void putAllConverter(Converter> converter) {
+ allConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
+ converter);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/NullableObjectConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/NullableObjectConverter.java
new file mode 100644
index 0000000..5989e49
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/NullableObjectConverter.java
@@ -0,0 +1,10 @@
+package ai.chat2db.excel.converters;
+
+/**
+ * When implementing convertToExcelData
method, pay attention to the reference value
may be
+ * null
+ *
+ * @author JiaJu Zhuang
+ **/
+public interface NullableObjectConverter extends Converter {
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ReadConverterContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ReadConverterContext.java
new file mode 100644
index 0000000..26f79ac
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/ReadConverterContext.java
@@ -0,0 +1,34 @@
+package ai.chat2db.excel.converters;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * read converter context
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@AllArgsConstructor
+public class ReadConverterContext {
+ /**
+ * Excel cell data.NotNull.
+ */
+ private ReadCellData readCellData;
+ /**
+ * Content property.Nullable.
+ */
+ private ExcelContentProperty contentProperty;
+ /**
+ * context.NotNull.
+ */
+ private AnalysisContext analysisContext;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/WriteConverterContext.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/WriteConverterContext.java
new file mode 100644
index 0000000..11b860f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/WriteConverterContext.java
@@ -0,0 +1,38 @@
+package ai.chat2db.excel.converters;
+
+import ai.chat2db.excel.context.WriteContext;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * write converter context
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+@AllArgsConstructor
+public class WriteConverterContext {
+
+ /**
+ * Java Data.NotNull.
+ */
+ private T value;
+
+ /**
+ * Content property.Nullable.
+ */
+ private ExcelContentProperty contentProperty;
+
+ /**
+ * write context
+ */
+ private WriteContext writeContext;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalBooleanConverter.java
new file mode 100644
index 0000000..e41f40a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.bigdecimal;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigDecimal and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigDecimalBooleanConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigDecimal.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public BigDecimal convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return BigDecimal.ONE;
+ }
+ return BigDecimal.ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (BigDecimal.ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalNumberConverter.java
new file mode 100644
index 0000000..31fb5c8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalNumberConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.bigdecimal;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigDecimal and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigDecimalNumberConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigDecimal.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public BigDecimal convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellData(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalStringConverter.java
new file mode 100644
index 0000000..1380831
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bigdecimal/BigDecimalStringConverter.java
@@ -0,0 +1,42 @@
+package ai.chat2db.excel.converters.bigdecimal;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigDecimal and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigDecimalStringConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigDecimal.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public BigDecimal convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseBigDecimal(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerBooleanConverter.java
new file mode 100644
index 0000000..7b309cc
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.biginteger;
+
+import java.math.BigInteger;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigInteger and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigIntegerBooleanConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigInteger.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public BigInteger convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return BigInteger.ONE;
+ }
+ return BigInteger.ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigInteger value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (BigInteger.ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerNumberConverter.java
new file mode 100644
index 0000000..8c6aa04
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerNumberConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.biginteger;
+
+import java.math.BigInteger;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigInteger and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigIntegerNumberConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigInteger.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public BigInteger convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().toBigInteger();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigInteger value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellData(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerStringConverter.java
new file mode 100644
index 0000000..77c0245
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/biginteger/BigIntegerStringConverter.java
@@ -0,0 +1,42 @@
+package ai.chat2db.excel.converters.biginteger;
+
+import java.math.BigInteger;
+import java.text.ParseException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * BigDecimal and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BigIntegerStringConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return BigInteger.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public BigInteger convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseBigDecimal(cellData.getStringValue(), contentProperty).toBigInteger();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(BigInteger value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanBooleanConverter.java
new file mode 100644
index 0000000..5e3f0d8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanBooleanConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.booleanconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Boolean and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BooleanBooleanConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Boolean.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Boolean convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getBooleanValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Boolean value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(value);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanNumberConverter.java
new file mode 100644
index 0000000..70c5971
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanNumberConverter.java
@@ -0,0 +1,46 @@
+package ai.chat2db.excel.converters.booleanconverter;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Boolean and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BooleanNumberConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Boolean.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Boolean convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (BigDecimal.ONE.compareTo(cellData.getNumberValue()) == 0) {
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Boolean value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (value) {
+ return new WriteCellData<>(BigDecimal.ONE);
+ }
+ return new WriteCellData<>(BigDecimal.ZERO);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanStringConverter.java
new file mode 100644
index 0000000..4879f6a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/booleanconverter/BooleanStringConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.booleanconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Boolean and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BooleanStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Boolean.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Boolean convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return Boolean.valueOf(cellData.getStringValue());
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Boolean value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(value.toString());
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/BoxingByteArrayImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/BoxingByteArrayImageConverter.java
new file mode 100644
index 0000000..f7b717a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/BoxingByteArrayImageConverter.java
@@ -0,0 +1,29 @@
+package ai.chat2db.excel.converters.bytearray;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Boxing Byte array and image converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class BoxingByteArrayImageConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Byte[].class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Byte[] value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ byte[] byteValue = new byte[value.length];
+ for (int i = 0; i < value.length; i++) {
+ byteValue[i] = value[i];
+ }
+ return new WriteCellData<>(byteValue);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/ByteArrayImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/ByteArrayImageConverter.java
new file mode 100644
index 0000000..455671e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/bytearray/ByteArrayImageConverter.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.converters.bytearray;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Byte array and image converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ByteArrayImageConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return byte[].class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(byte[] value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(value);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteBooleanConverter.java
new file mode 100644
index 0000000..74cb2d8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.byteconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Byte and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ByteBooleanConverter implements Converter {
+ private static final Byte ONE = (byte)1;
+ private static final Byte ZERO = (byte)0;
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Byte.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Byte convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Byte value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteNumberConverter.java
new file mode 100644
index 0000000..9ee9a96
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteNumberConverter.java
@@ -0,0 +1,40 @@
+package ai.chat2db.excel.converters.byteconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Byte and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ByteNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Byte.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Byte convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().byteValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Byte value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellData(value, contentProperty);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteStringConverter.java
new file mode 100644
index 0000000..12ed12a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/byteconverter/ByteStringConverter.java
@@ -0,0 +1,42 @@
+package ai.chat2db.excel.converters.byteconverter;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Byte and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ByteStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Byte.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Byte convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseByte(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Byte value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateDateConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateDateConverter.java
new file mode 100644
index 0000000..693a5fb
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateDateConverter.java
@@ -0,0 +1,34 @@
+package ai.chat2db.excel.converters.date;
+
+import java.util.Date;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.WorkBookUtil;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Date and date converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DateDateConverter implements Converter {
+ @Override
+ public Class supportJavaTypeKey() {
+ return Date.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Date value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws Exception {
+ WriteCellData> cellData = new WriteCellData<>(value);
+ String format = null;
+ if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
+ format = contentProperty.getDateTimeFormatProperty().getFormat();
+ }
+ WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat);
+ return cellData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateNumberConverter.java
new file mode 100644
index 0000000..831fef9
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateNumberConverter.java
@@ -0,0 +1,56 @@
+package ai.chat2db.excel.converters.date;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+
+/**
+ * Date and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DateNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Date.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Date convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(),
+ globalConfiguration.getUse1904windowing());
+ } else {
+ return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(),
+ contentProperty.getDateTimeFormatProperty().getUse1904windowing());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Date value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(
+ BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing())));
+ } else {
+ return new WriteCellData<>(BigDecimal.valueOf(
+ DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing())));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateStringConverter.java
new file mode 100644
index 0000000..8b23c7a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/date/DateStringConverter.java
@@ -0,0 +1,50 @@
+package ai.chat2db.excel.converters.date;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Date and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DateStringConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Date.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Date convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.parseDate(cellData.getStringValue(), null);
+ } else {
+ return DateUtils.parseDate(cellData.getStringValue(),
+ contentProperty.getDateTimeFormatProperty().getFormat());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Date value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(DateUtils.format(value, null));
+ } else {
+ return new WriteCellData<>(DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat()));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleBooleanConverter.java
new file mode 100644
index 0000000..c78396d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.doubleconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Double and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DoubleBooleanConverter implements Converter {
+ private static final Double ONE = 1.0;
+ private static final Double ZERO = 0.0;
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Double.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Double convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Double value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleNumberConverter.java
new file mode 100644
index 0000000..7b55e1f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleNumberConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.doubleconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Double and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DoubleNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Double.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Double convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().doubleValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Double value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellData(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleStringConverter.java
new file mode 100644
index 0000000..3ae0529
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/doubleconverter/DoubleStringConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.doubleconverter;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Double and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class DoubleStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Double.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Double convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseDouble(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Double value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/file/FileImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/file/FileImageConverter.java
new file mode 100644
index 0000000..b9476dd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/file/FileImageConverter.java
@@ -0,0 +1,28 @@
+package ai.chat2db.excel.converters.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.FileUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * File and image converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class FileImageConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return File.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(File value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws IOException {
+ return new WriteCellData<>(FileUtils.readFileToByteArray(value));
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatBooleanConverter.java
new file mode 100644
index 0000000..4833b9b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.floatconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Float and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class FloatBooleanConverter implements Converter {
+ private static final Float ONE = (float)1.0;
+ private static final Float ZERO = (float)0.0;
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Float.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Float convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Float value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatNumberConverter.java
new file mode 100644
index 0000000..413d2b2
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatNumberConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.floatconverter;
+
+import ai.chat2db.excel.converters.WriteConverterContext;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Float and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class FloatNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Float.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Float convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().floatValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(WriteConverterContext context) {
+ return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty());
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatStringConverter.java
new file mode 100644
index 0000000..9d6125d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/floatconverter/FloatStringConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.floatconverter;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Float and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class FloatStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Float.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Float convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseFloat(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Float value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/inputstream/InputStreamImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/inputstream/InputStreamImageConverter.java
new file mode 100644
index 0000000..5bfaf7b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/inputstream/InputStreamImageConverter.java
@@ -0,0 +1,29 @@
+package ai.chat2db.excel.converters.inputstream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.IoUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * File and image converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class InputStreamImageConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return InputStream.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(InputStream value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws IOException {
+ return new WriteCellData<>(IoUtils.toByteArray(value));
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerBooleanConverter.java
new file mode 100644
index 0000000..1dc064b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.integer;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Integer and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class IntegerBooleanConverter implements Converter {
+ private static final Integer ONE = 1;
+ private static final Integer ZERO = 0;
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Integer.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Integer convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerNumberConverter.java
new file mode 100644
index 0000000..0aff401
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerNumberConverter.java
@@ -0,0 +1,40 @@
+package ai.chat2db.excel.converters.integer;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.converters.WriteConverterContext;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Integer and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class IntegerNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Integer.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Integer convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().intValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(WriteConverterContext context) {
+ return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty());
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerStringConverter.java
new file mode 100644
index 0000000..2c53aa4
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/integer/IntegerStringConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.integer;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Integer and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class IntegerStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Integer.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Integer convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseInteger(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateDateConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateDateConverter.java
new file mode 100644
index 0000000..fe42346
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateDateConverter.java
@@ -0,0 +1,36 @@
+package ai.chat2db.excel.converters.localdate;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.WorkBookUtil;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * LocalDate and date converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateDateConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDate.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws Exception {
+ LocalDateTime localDateTime = value == null ? null : value.atTime(0, 0);
+ WriteCellData> cellData = new WriteCellData<>(localDateTime);
+ String format = null;
+ if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
+ format = contentProperty.getDateTimeFormatProperty().getFormat();
+ }
+ WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultLocalDateFormat);
+ return cellData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateNumberConverter.java
new file mode 100644
index 0000000..65945bd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateNumberConverter.java
@@ -0,0 +1,56 @@
+package ai.chat2db.excel.converters.localdate;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+
+/**
+ * LocalDate and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDate.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public LocalDate convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.getLocalDate(cellData.getNumberValue().doubleValue(),
+ globalConfiguration.getUse1904windowing());
+ } else {
+ return DateUtils.getLocalDate(cellData.getNumberValue().doubleValue(),
+ contentProperty.getDateTimeFormatProperty().getUse1904windowing());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(
+ BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing())));
+ } else {
+ return new WriteCellData<>(BigDecimal.valueOf(
+ DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing())));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateStringConverter.java
new file mode 100644
index 0000000..b5dab1f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdate/LocalDateStringConverter.java
@@ -0,0 +1,52 @@
+package ai.chat2db.excel.converters.localdate;
+
+import java.text.ParseException;
+import java.time.LocalDate;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * LocalDate and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateStringConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDate.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public LocalDate convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.parseLocalDate(cellData.getStringValue(), null, globalConfiguration.getLocale());
+ } else {
+ return DateUtils.parseLocalDate(cellData.getStringValue(),
+ contentProperty.getDateTimeFormatProperty().getFormat(), globalConfiguration.getLocale());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(DateUtils.format(value, null, globalConfiguration.getLocale()));
+ } else {
+ return new WriteCellData<>(
+ DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat(),
+ globalConfiguration.getLocale()));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeDateConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeDateConverter.java
new file mode 100644
index 0000000..534cce0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeDateConverter.java
@@ -0,0 +1,34 @@
+package ai.chat2db.excel.converters.localdatetime;
+
+import java.time.LocalDateTime;
+
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.WorkBookUtil;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * LocalDateTime and date converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateTimeDateConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDateTime.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws Exception {
+ WriteCellData> cellData = new WriteCellData<>(value);
+ String format = null;
+ if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
+ format = contentProperty.getDateTimeFormatProperty().getFormat();
+ }
+ WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat);
+ return cellData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeNumberConverter.java
new file mode 100644
index 0000000..afc9136
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeNumberConverter.java
@@ -0,0 +1,56 @@
+package ai.chat2db.excel.converters.localdatetime;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+
+/**
+ * LocalDateTime and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateTimeNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDateTime.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public LocalDateTime convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
+ globalConfiguration.getUse1904windowing());
+ } else {
+ return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
+ contentProperty.getDateTimeFormatProperty().getUse1904windowing());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(
+ BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing())));
+ } else {
+ return new WriteCellData<>(BigDecimal.valueOf(
+ DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing())));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeStringConverter.java
new file mode 100644
index 0000000..f4939e5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/localdatetime/LocalDateTimeStringConverter.java
@@ -0,0 +1,52 @@
+package ai.chat2db.excel.converters.localdatetime;
+
+import java.text.ParseException;
+import java.time.LocalDateTime;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * LocalDateTime and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LocalDateTimeStringConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return LocalDateTime.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public LocalDateTime convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return DateUtils.parseLocalDateTime(cellData.getStringValue(), null, globalConfiguration.getLocale());
+ } else {
+ return DateUtils.parseLocalDateTime(cellData.getStringValue(),
+ contentProperty.getDateTimeFormatProperty().getFormat(), globalConfiguration.getLocale());
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
+ return new WriteCellData<>(DateUtils.format(value, null, globalConfiguration.getLocale()));
+ } else {
+ return new WriteCellData<>(
+ DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat(),
+ globalConfiguration.getLocale()));
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongBooleanConverter.java
new file mode 100644
index 0000000..d71f58f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.longconverter;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Long and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LongBooleanConverter implements Converter {
+ private static final Long ONE = 1L;
+ private static final Long ZERO = 0L;
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Long.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Long convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Long value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongNumberConverter.java
new file mode 100644
index 0000000..42443b5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongNumberConverter.java
@@ -0,0 +1,40 @@
+package ai.chat2db.excel.converters.longconverter;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.converters.WriteConverterContext;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Long and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LongNumberConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Long.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Long convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().longValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(WriteConverterContext context) {
+ return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty());
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongStringConverter.java
new file mode 100644
index 0000000..2907dfd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/longconverter/LongStringConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.longconverter;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Long and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class LongStringConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Long.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Long convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseLong(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Long value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortBooleanConverter.java
new file mode 100644
index 0000000..f08f7b6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortBooleanConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.shortconverter;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Short and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ShortBooleanConverter implements Converter {
+ private static final Short ONE = 1;
+ private static final Short ZERO = 0;
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Short.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public Short convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (cellData.getBooleanValue()) {
+ return ONE;
+ }
+ return ZERO;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Short value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ if (ONE.equals(value)) {
+ return new WriteCellData<>(Boolean.TRUE);
+ }
+ return new WriteCellData<>(Boolean.FALSE);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortNumberConverter.java
new file mode 100644
index 0000000..285e802
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortNumberConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.shortconverter;
+
+import ai.chat2db.excel.converters.WriteConverterContext;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Short and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ShortNumberConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Short.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public Short convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getNumberValue().shortValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(WriteConverterContext context) {
+ return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty());
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortStringConverter.java
new file mode 100644
index 0000000..be61f82
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/shortconverter/ShortStringConverter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.converters.shortconverter;
+
+import java.text.ParseException;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Short and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ShortStringConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return Short.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Short convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws ParseException {
+ return NumberUtils.parseShort(cellData.getStringValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Short value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return NumberUtils.formatToCellDataString(value, contentProperty);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringBooleanConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringBooleanConverter.java
new file mode 100644
index 0000000..e5b6817
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringBooleanConverter.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.converters.string;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * String and boolean converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class StringBooleanConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return String.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.BOOLEAN;
+ }
+
+ @Override
+ public String convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getBooleanValue().toString();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(Boolean.valueOf(value));
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringErrorConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringErrorConverter.java
new file mode 100644
index 0000000..45204c7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringErrorConverter.java
@@ -0,0 +1,38 @@
+package ai.chat2db.excel.converters.string;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * String and error converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class StringErrorConverter implements Converter {
+ @Override
+ public Class supportJavaTypeKey() {
+ return String.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.ERROR;
+ }
+
+ @Override
+ public String convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getStringValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(CellDataTypeEnum.ERROR, value);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringImageConverter.java
new file mode 100644
index 0000000..869be84
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringImageConverter.java
@@ -0,0 +1,29 @@
+package ai.chat2db.excel.converters.string;
+
+import java.io.File;
+import java.io.IOException;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.FileUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * String and image converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class StringImageConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return String.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws IOException {
+ return new WriteCellData<>(FileUtils.readFileToByteArray(new File(value)));
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringNumberConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringNumberConverter.java
new file mode 100644
index 0000000..29651b2
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringNumberConverter.java
@@ -0,0 +1,63 @@
+package ai.chat2db.excel.converters.string;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.NumberDataFormatterUtils;
+import ai.chat2db.excel.util.NumberUtils;
+import ai.chat2db.excel.util.StringUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * String and number converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class StringNumberConverter implements Converter {
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return String.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.NUMBER;
+ }
+
+ @Override
+ public String convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ // If there are "DateTimeFormat", read as date
+ if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
+ return DateUtils.format(cellData.getNumberValue(),
+ contentProperty.getDateTimeFormatProperty().getUse1904windowing(),
+ contentProperty.getDateTimeFormatProperty().getFormat());
+ }
+ // If there are "NumberFormat", read as number
+ if (contentProperty != null && contentProperty.getNumberFormatProperty() != null) {
+ return NumberUtils.format(cellData.getNumberValue(), contentProperty);
+ }
+ // Excel defines formatting
+ boolean hasDataFormatData = cellData.getDataFormatData() != null
+ && cellData.getDataFormatData().getIndex() != null && !StringUtils.isEmpty(
+ cellData.getDataFormatData().getFormat());
+ if (hasDataFormatData) {
+ return NumberDataFormatterUtils.format(cellData.getNumberValue(),
+ cellData.getDataFormatData().getIndex(), cellData.getDataFormatData().getFormat(), globalConfiguration);
+ }
+ // Default conversion number
+ return NumberUtils.format(cellData.getNumberValue(), contentProperty);
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(new BigDecimal(value));
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringStringConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringStringConverter.java
new file mode 100644
index 0000000..a0aef73
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/string/StringStringConverter.java
@@ -0,0 +1,38 @@
+package ai.chat2db.excel.converters.string;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * String and string converter
+ *
+ * @author Jiaju Zhuang
+ */
+public class StringStringConverter implements Converter {
+ @Override
+ public Class> supportJavaTypeKey() {
+ return String.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public String convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return cellData.getStringValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(String value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>(value);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/url/UrlImageConverter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/url/UrlImageConverter.java
new file mode 100644
index 0000000..72aedca
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/converters/url/UrlImageConverter.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.converters.url;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.util.IoUtils;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.data.WriteCellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Url and image converter
+ *
+ * @author Jiaju Zhuang
+ * @since 2.1.1
+ */
+public class UrlImageConverter implements Converter {
+ public static int urlConnectTimeout = 1000;
+ public static int urlReadTimeout = 5000;
+
+ @Override
+ public Class> supportJavaTypeKey() {
+ return URL.class;
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(URL value, ExcelContentProperty contentProperty,
+ GlobalConfiguration globalConfiguration) throws IOException {
+ InputStream inputStream = null;
+ try {
+ URLConnection urlConnection = value.openConnection();
+ urlConnection.setConnectTimeout(urlConnectTimeout);
+ urlConnection.setReadTimeout(urlReadTimeout);
+ inputStream = urlConnection.getInputStream();
+ byte[] bytes = IoUtils.toByteArray(inputStream);
+ return new WriteCellData<>(bytes);
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/BooleanEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/BooleanEnum.java
new file mode 100644
index 0000000..bc585c7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/BooleanEnum.java
@@ -0,0 +1,33 @@
+package ai.chat2db.excel.enums;
+
+import lombok.Getter;
+
+/**
+ * Default values cannot be used for annotations.
+ * So an additional an enumeration to determine whether the user has added the enumeration.
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum BooleanEnum {
+ /**
+ * NULL
+ */
+ DEFAULT(null),
+ /**
+ * TRUE
+ */
+ TRUE(Boolean.TRUE),
+ /**
+ * FALSE
+ */
+ FALSE(Boolean.FALSE),
+ ;
+
+ Boolean booleanValue;
+
+ BooleanEnum(Boolean booleanValue) {
+ this.booleanValue = booleanValue;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ByteOrderMarkEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ByteOrderMarkEnum.java
new file mode 100644
index 0000000..1fb29bf
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ByteOrderMarkEnum.java
@@ -0,0 +1,66 @@
+package ai.chat2db.excel.enums;
+
+import java.nio.charset.Charset;
+import java.util.Map;
+
+import ai.chat2db.excel.util.MapUtils;
+
+import lombok.Getter;
+import org.apache.commons.io.ByteOrderMark;
+
+/**
+ * byte order mark
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum ByteOrderMarkEnum {
+
+ /**
+ * UTF_8
+ */
+ UTF_8(ByteOrderMark.UTF_8),
+ /**
+ * UTF_16BE
+ */
+ UTF_16BE(ByteOrderMark.UTF_16BE),
+ /**
+ * UTF_16LE
+ */
+ UTF_16LE(ByteOrderMark.UTF_16LE),
+ /**
+ * UTF_32BE
+ */
+ UTF_32BE(ByteOrderMark.UTF_32BE),
+ /**
+ * UTF_32LE
+ */
+ UTF_32LE(ByteOrderMark.UTF_32LE),
+
+ ;
+
+ final ByteOrderMark byteOrderMark;
+ final String stringPrefix;
+
+ ByteOrderMarkEnum(ByteOrderMark byteOrderMark) {
+ this.byteOrderMark = byteOrderMark;
+ Charset charset = Charset.forName(byteOrderMark.getCharsetName());
+ this.stringPrefix = new String(byteOrderMark.getBytes(), charset);
+ }
+
+ /**
+ * store character aliases corresponding to `ByteOrderMark` prefix
+ */
+ private static final Map CHARSET_BYTE_ORDER_MARK_MAP = MapUtils.newHashMap();
+
+ static {
+ for (ByteOrderMarkEnum value : ByteOrderMarkEnum.values()) {
+ CHARSET_BYTE_ORDER_MARK_MAP.put(value.getByteOrderMark().getCharsetName(), value);
+ }
+ }
+
+ public static ByteOrderMarkEnum valueOfByCharsetName(String charsetName) {
+ return CHARSET_BYTE_ORDER_MARK_MAP.get(charsetName);
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CacheLocationEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CacheLocationEnum.java
new file mode 100644
index 0000000..edb1cf2
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CacheLocationEnum.java
@@ -0,0 +1,23 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * cache locaciton
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum CacheLocationEnum {
+ /**
+ * The cache will be stored in {@code ThreadLocal}, and will be cleared when the excel read and write is completed.
+ */
+ THREAD_LOCAL,
+
+ /**
+ * The cache will not be cleared unless the app is stopped.
+ */
+ MEMORY,
+
+ /**
+ * No caching.It may lose some of performance.
+ */
+ NONE;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellDataTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellDataTypeEnum.java
new file mode 100644
index 0000000..0e8d069
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellDataTypeEnum.java
@@ -0,0 +1,72 @@
+package ai.chat2db.excel.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ai.chat2db.excel.util.StringUtils;
+
+/**
+ * excel internal data type
+ *
+ * @author Jiaju Zhuang
+ */
+public enum CellDataTypeEnum {
+ /**
+ * string
+ */
+ STRING,
+ /**
+ * This type of data does not need to be read in the 'sharedStrings.xml', it is only used for overuse, and the data
+ * will be stored as a {@link #STRING}
+ */
+ DIRECT_STRING,
+ /**
+ * number
+ */
+ NUMBER,
+ /**
+ * boolean
+ */
+ BOOLEAN,
+ /**
+ * empty
+ */
+ EMPTY,
+ /**
+ * error
+ */
+ ERROR,
+ /**
+ * date. Support only when writing.
+ */
+ DATE,
+ /**
+ * rich text string.Support only when writing.
+ */
+ RICH_TEXT_STRING,
+ ;
+
+ private static final Map TYPE_ROUTING_MAP = new HashMap(16);
+
+ static {
+ TYPE_ROUTING_MAP.put("s", STRING);
+ TYPE_ROUTING_MAP.put("str", DIRECT_STRING);
+ TYPE_ROUTING_MAP.put("inlineStr", DIRECT_STRING);
+ TYPE_ROUTING_MAP.put("e", ERROR);
+ TYPE_ROUTING_MAP.put("b", BOOLEAN);
+ TYPE_ROUTING_MAP.put("n", NUMBER);
+ }
+
+ /**
+ * Build data types
+ *
+ * @param cellType
+ * @return
+ */
+ public static CellDataTypeEnum buildFromCellType(String cellType) {
+ if (StringUtils.isEmpty(cellType)) {
+ return EMPTY;
+ }
+ return TYPE_ROUTING_MAP.get(cellType);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellExtraTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellExtraTypeEnum.java
new file mode 100644
index 0000000..ac61cd4
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/CellExtraTypeEnum.java
@@ -0,0 +1,21 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * Extra data type
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum CellExtraTypeEnum {
+ /**
+ * Comment
+ */
+ COMMENT,
+ /**
+ * Hyperlink
+ */
+ HYPERLINK,
+ /**
+ * Merge
+ */
+ MERGE,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HeadKindEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HeadKindEnum.java
new file mode 100644
index 0000000..b8a30c4
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HeadKindEnum.java
@@ -0,0 +1,21 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * The types of header
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum HeadKindEnum {
+ /**
+ * none
+ */
+ NONE,
+ /**
+ * class
+ */
+ CLASS,
+ /**
+ * String
+ */
+ STRING;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HolderEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HolderEnum.java
new file mode 100644
index 0000000..b705753
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/HolderEnum.java
@@ -0,0 +1,25 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * The types of holder
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum HolderEnum {
+ /**
+ * workbook
+ */
+ WORKBOOK,
+ /**
+ * sheet
+ */
+ SHEET,
+ /**
+ * table
+ */
+ TABLE,
+ /**
+ * row
+ */
+ ROW;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/NumericCellTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/NumericCellTypeEnum.java
new file mode 100644
index 0000000..43a1903
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/NumericCellTypeEnum.java
@@ -0,0 +1,22 @@
+package ai.chat2db.excel.enums;
+
+import org.apache.poi.ss.usermodel.CellType;
+
+/**
+ * Used to supplement {@link CellType}.
+ *
+ * Cannot distinguish between date and number in write case.
+ *
+ * @author Jiaju Zhuang
+ */
+public enum NumericCellTypeEnum {
+ /**
+ * number
+ */
+ NUMBER,
+ /**
+ * date. Support only when writing.
+ */
+ DATE,
+ ;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ReadDefaultReturnEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ReadDefaultReturnEnum.java
new file mode 100644
index 0000000..2d48a0f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/ReadDefaultReturnEnum.java
@@ -0,0 +1,37 @@
+package ai.chat2db.excel.enums;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import ai.chat2db.excel.metadata.data.ReadCellData;
+
+/**
+ * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
+ *
+ * @author Jiaju Zhuang
+ */
+public enum ReadDefaultReturnEnum {
+ /**
+ * default.The content of cells into string, is the same as you see in the excel.
+ */
+ STRING,
+
+ /**
+ * Returns the actual type.
+ * Will be automatically selected according to the cell contents what return type, will return the following class:
+ *
+ * {@link BigDecimal}
+ * {@link Boolean}
+ * {@link String}
+ * {@link LocalDateTime}
+ *
+ */
+ ACTUAL_DATA,
+
+ /**
+ * Return to {@link ReadCellData}, can decide which field you need.
+ */
+ READ_CELL_DATA,
+ ;
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/RowTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/RowTypeEnum.java
new file mode 100644
index 0000000..0ec7cef
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/RowTypeEnum.java
@@ -0,0 +1,17 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * The types of row
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum RowTypeEnum {
+ /**
+ * data
+ */
+ DATA,
+ /**
+ * empty
+ */
+ EMPTY,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteDirectionEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteDirectionEnum.java
new file mode 100644
index 0000000..e29fdc6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteDirectionEnum.java
@@ -0,0 +1,17 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * Direction of writing
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum WriteDirectionEnum {
+ /**
+ * Vertical write.
+ */
+ VERTICAL,
+ /**
+ * Horizontal write.
+ */
+ HORIZONTAL,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteLastRowTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteLastRowTypeEnum.java
new file mode 100644
index 0000000..a9f04e1
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteLastRowTypeEnum.java
@@ -0,0 +1,21 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * The types of write last row
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum WriteLastRowTypeEnum {
+ /**
+ * Excel are created without templates ,And any data has been written;
+ */
+ COMMON_EMPTY,
+ /**
+ * Excel are created with templates ,And any data has been written;
+ */
+ TEMPLATE_EMPTY,
+ /**
+ * Any data has been written;
+ */
+ HAS_DATA,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTemplateAnalysisCellTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTemplateAnalysisCellTypeEnum.java
new file mode 100644
index 0000000..ddd0b39
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTemplateAnalysisCellTypeEnum.java
@@ -0,0 +1,17 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * Type of template to read when writing
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum WriteTemplateAnalysisCellTypeEnum {
+ /**
+ * Common field.
+ */
+ COMMON,
+ /**
+ * A collection of fields.
+ */
+ COLLECTION,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTypeEnum.java
new file mode 100644
index 0000000..bdfdd75
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/WriteTypeEnum.java
@@ -0,0 +1,17 @@
+package ai.chat2db.excel.enums;
+
+/**
+ * Enumeration of write methods
+ *
+ * @author Jiaju Zhuang
+ **/
+public enum WriteTypeEnum {
+ /**
+ * Add.
+ */
+ ADD,
+ /**
+ * Fill.
+ */
+ FILL,;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/BorderStyleEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/BorderStyleEnum.java
new file mode 100644
index 0000000..ad0f04d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/BorderStyleEnum.java
@@ -0,0 +1,95 @@
+package ai.chat2db.excel.enums.poi;
+
+import lombok.Getter;
+import org.apache.poi.ss.usermodel.BorderStyle;
+
+/**
+ * The enumeration value indicating the line style of a border in a cell,
+ * i.e., whether it is bordered dash dot, dash dot dot, dashed, dotted, double, hair, medium,
+ * medium dash dot, medium dash dot dot, medium dashed, none, slant dash dot, thick or thin.
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum BorderStyleEnum {
+ /**
+ * null
+ */
+ DEFAULT(null),
+
+ /**
+ * No border (default)
+ */
+ NONE(BorderStyle.NONE),
+
+ /**
+ * Thin border
+ */
+ THIN(BorderStyle.THIN),
+
+ /**
+ * Medium border
+ */
+ MEDIUM(BorderStyle.MEDIUM),
+
+ /**
+ * dash border
+ */
+ DASHED(BorderStyle.DASHED),
+
+ /**
+ * dot border
+ */
+ DOTTED(BorderStyle.DOTTED),
+
+ /**
+ * Thick border
+ */
+ THICK(BorderStyle.THICK),
+
+ /**
+ * double-line border
+ */
+ DOUBLE(BorderStyle.DOUBLE),
+
+ /**
+ * hair-line border
+ */
+ HAIR(BorderStyle.HAIR),
+
+ /**
+ * Medium dashed border
+ */
+ MEDIUM_DASHED(BorderStyle.MEDIUM_DASHED),
+
+ /**
+ * dash-dot border
+ */
+ DASH_DOT(BorderStyle.DASH_DOT),
+
+ /**
+ * medium dash-dot border
+ */
+ MEDIUM_DASH_DOT(BorderStyle.MEDIUM_DASH_DOT),
+
+ /**
+ * dash-dot-dot border
+ */
+ DASH_DOT_DOT(BorderStyle.DASH_DOT_DOT),
+
+ /**
+ * medium dash-dot-dot border
+ */
+ MEDIUM_DASH_DOT_DOT(BorderStyle.MEDIUM_DASH_DOT_DOT),
+
+ /**
+ * slanted dash-dot border
+ */
+ SLANTED_DASH_DOT(BorderStyle.SLANTED_DASH_DOT);
+
+ BorderStyle poiBorderStyle;
+
+ BorderStyleEnum(BorderStyle poiBorderStyle) {
+ this.poiBorderStyle = poiBorderStyle;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/FillPatternTypeEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/FillPatternTypeEnum.java
new file mode 100644
index 0000000..789cdbd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/FillPatternTypeEnum.java
@@ -0,0 +1,119 @@
+package ai.chat2db.excel.enums.poi;
+
+import lombok.Getter;
+import org.apache.poi.ss.usermodel.FillPatternType;
+
+/**
+ * The enumeration value indicating the style of fill pattern being used for a cell format.
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum FillPatternTypeEnum {
+
+ /**
+ * null
+ */
+ DEFAULT(null),
+
+ /**
+ * No background
+ */
+ NO_FILL(FillPatternType.NO_FILL),
+
+ /**
+ * Solidly filled
+ */
+ SOLID_FOREGROUND(FillPatternType.SOLID_FOREGROUND),
+
+ /**
+ * Small fine dots
+ */
+ FINE_DOTS(FillPatternType.FINE_DOTS),
+
+ /**
+ * Wide dots
+ */
+ ALT_BARS(FillPatternType.ALT_BARS),
+
+ /**
+ * Sparse dots
+ */
+ SPARSE_DOTS(FillPatternType.SPARSE_DOTS),
+
+ /**
+ * Thick horizontal bands
+ */
+ THICK_HORZ_BANDS(FillPatternType.THICK_HORZ_BANDS),
+
+ /**
+ * Thick vertical bands
+ */
+ THICK_VERT_BANDS(FillPatternType.THICK_VERT_BANDS),
+
+ /**
+ * Thick backward facing diagonals
+ */
+ THICK_BACKWARD_DIAG(FillPatternType.THICK_BACKWARD_DIAG),
+
+ /**
+ * Thick forward facing diagonals
+ */
+ THICK_FORWARD_DIAG(FillPatternType.THICK_FORWARD_DIAG),
+
+ /**
+ * Large spots
+ */
+ BIG_SPOTS(FillPatternType.BIG_SPOTS),
+
+ /**
+ * Brick-like layout
+ */
+ BRICKS(FillPatternType.BRICKS),
+
+ /**
+ * Thin horizontal bands
+ */
+ THIN_HORZ_BANDS(FillPatternType.THIN_HORZ_BANDS),
+
+ /**
+ * Thin vertical bands
+ */
+ THIN_VERT_BANDS(FillPatternType.THIN_VERT_BANDS),
+
+ /**
+ * Thin backward diagonal
+ */
+ THIN_BACKWARD_DIAG(FillPatternType.THIN_BACKWARD_DIAG),
+
+ /**
+ * Thin forward diagonal
+ */
+ THIN_FORWARD_DIAG(FillPatternType.THIN_FORWARD_DIAG),
+
+ /**
+ * Squares
+ */
+ SQUARES(FillPatternType.SQUARES),
+
+ /**
+ * Diamonds
+ */
+ DIAMONDS(FillPatternType.DIAMONDS),
+
+ /**
+ * Less Dots
+ */
+ LESS_DOTS(FillPatternType.LESS_DOTS),
+
+ /**
+ * Least Dots
+ */
+ LEAST_DOTS(FillPatternType.LEAST_DOTS);
+
+ FillPatternType poiFillPatternType;
+
+ FillPatternTypeEnum(FillPatternType poiFillPatternType) {
+ this.poiFillPatternType = poiFillPatternType;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/HorizontalAlignmentEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/HorizontalAlignmentEnum.java
new file mode 100644
index 0000000..4b5418c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/HorizontalAlignmentEnum.java
@@ -0,0 +1,93 @@
+package ai.chat2db.excel.enums.poi;
+
+import lombok.Getter;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+
+/**
+ * The enumeration value indicating horizontal alignment of a cell,
+ * i.e., whether it is aligned general, left, right, horizontally centered, filled (replicated),
+ * justified, centered across multiple cells, or distributed.
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum HorizontalAlignmentEnum {
+ /**
+ * null
+ */
+ DEFAULT(null),
+ /**
+ * The horizontal alignment is general-aligned. Text data is left-aligned.
+ * Numbers, dates, and times are rightaligned. Boolean types are centered.
+ * Changing the alignment does not change the type of data.
+ */
+ GENERAL(HorizontalAlignment.GENERAL),
+
+ /**
+ * The horizontal alignment is left-aligned, even in Rightto-Left mode.
+ * Aligns contents at the left edge of the cell. If an indent amount is specified, the contents of
+ * the cell is indented from the left by the specified number of character spaces. The character spaces are
+ * based on the default font and font size for the workbook.
+ */
+ LEFT(HorizontalAlignment.LEFT),
+
+ /**
+ * The horizontal alignment is centered, meaning the text is centered across the cell.
+ */
+ CENTER(HorizontalAlignment.CENTER),
+
+ /**
+ * The horizontal alignment is right-aligned, meaning that cell contents are aligned at the right edge of the cell,
+ * even in Right-to-Left mode.
+ */
+ RIGHT(HorizontalAlignment.RIGHT),
+
+ /**
+ * Indicates that the value of the cell should be filled
+ * across the entire width of the cell. If blank cells to the right also have the fill alignment,
+ * they are also filled with the value, using a convention similar to centerContinuous.
+ *
+ * Additional rules:
+ *
+ * Only whole values can be appended, not partial values.
+ * The column will not be widened to 'best fit' the filled value
+ * If appending an additional occurrence of the value exceeds the boundary of the cell
+ * left/right edge, don't append the additional occurrence of the value.
+ * The display value of the cell is filled, not the underlying raw number.
+ *
+ */
+ FILL(HorizontalAlignment.FILL),
+
+ /**
+ * The horizontal alignment is justified (flush left and right).
+ * For each line of text, aligns each line of the wrapped text in a cell to the right and left
+ * (except the last line). If no single line of text wraps in the cell, then the text is not justified.
+ */
+ JUSTIFY(HorizontalAlignment.JUSTIFY),
+
+ /**
+ * The horizontal alignment is centered across multiple cells.
+ * The information about how many cells to span is expressed in the Sheet Part,
+ * in the row of the cell in question. For each cell that is spanned in the alignment,
+ * a cell element needs to be written out, with the same style Id which references the centerContinuous alignment.
+ */
+ CENTER_SELECTION(HorizontalAlignment.CENTER_SELECTION),
+
+ /**
+ * Indicates that each 'word' in each line of text inside the cell is evenly distributed
+ * across the width of the cell, with flush right and left margins.
+ *
+ * When there is also an indent value to apply, both the left and right side of the cell
+ * are padded by the indent value.
+ *
+ * A 'word' is a set of characters with no space character in them.
+ * Two lines inside a cell are separated by a carriage return.
+ */
+ DISTRIBUTED(HorizontalAlignment.DISTRIBUTED);
+
+ HorizontalAlignment poiHorizontalAlignment;
+
+ HorizontalAlignmentEnum(HorizontalAlignment poiHorizontalAlignment) {
+ this.poiHorizontalAlignment = poiHorizontalAlignment;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/VerticalAlignmentEnum.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/VerticalAlignmentEnum.java
new file mode 100644
index 0000000..4ddeea8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/enums/poi/VerticalAlignmentEnum.java
@@ -0,0 +1,71 @@
+package ai.chat2db.excel.enums.poi;
+
+import lombok.Getter;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+
+/**
+ * This enumeration value indicates the type of vertical alignment for a cell, i.e.,
+ * whether it is aligned top, bottom, vertically centered, justified or distributed.
+ *
+ *
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+public enum VerticalAlignmentEnum {
+ /**
+ * null
+ */
+ DEFAULT(null),
+ /**
+ * The vertical alignment is aligned-to-top.
+ */
+ TOP(VerticalAlignment.TOP),
+
+ /**
+ * The vertical alignment is centered across the height of the cell.
+ */
+ CENTER(VerticalAlignment.CENTER),
+
+ /**
+ * The vertical alignment is aligned-to-bottom. (typically the default value)
+ */
+ BOTTOM(VerticalAlignment.BOTTOM),
+
+ /**
+ *
+ * When text direction is horizontal: the vertical alignment of lines of text is distributed vertically,
+ * where each line of text inside the cell is evenly distributed across the height of the cell,
+ * with flush top and bottom margins.
+ *
+ *
+ * When text direction is vertical: similar behavior as horizontal justification.
+ * The alignment is justified (flush top and bottom in this case). For each line of text, each
+ * line of the wrapped text in a cell is aligned to the top and bottom (except the last line).
+ * If no single line of text wraps in the cell, then the text is not justified.
+ *
+ */
+ JUSTIFY(VerticalAlignment.JUSTIFY),
+
+ /**
+ *
+ * When text direction is horizontal: the vertical alignment of lines of text is distributed vertically,
+ * where each line of text inside the cell is evenly distributed across the height of the cell,
+ * with flush top
+ *
+ *
+ * When text direction is vertical: behaves exactly as distributed horizontal alignment.
+ * The first words in a line of text (appearing at the top of the cell) are flush
+ * with the top edge of the cell, and the last words of a line of text are flush with the bottom edge of the cell,
+ * and the line of text is distributed evenly from top to bottom.
+ *
+ */
+ DISTRIBUTED(VerticalAlignment.DISTRIBUTED);
+
+ VerticalAlignment poiVerticalAlignmentEnum;
+
+ VerticalAlignmentEnum(VerticalAlignment poiVerticalAlignmentEnum) {
+ this.poiVerticalAlignmentEnum = poiVerticalAlignmentEnum;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AbstractIgnoreExceptionReadListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AbstractIgnoreExceptionReadListener.java
new file mode 100644
index 0000000..3e9b2f3
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AbstractIgnoreExceptionReadListener.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.event;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.metadata.CellExtra;
+import ai.chat2db.excel.read.listener.ReadListener;
+
+/**
+ * Receives the return of each piece of data parsed
+ *
+ * @author jipengfei
+ * @deprecated Use directly {@link ReadListener}
+ */
+@Deprecated
+public abstract class AbstractIgnoreExceptionReadListener implements ReadListener {
+
+ /**
+ * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
+ * entire read will terminate.
+ *
+ * @param exception
+ * @param context
+ */
+ @Override
+ public void onException(Exception exception, AnalysisContext context) {}
+
+ /**
+ * The current method is called when extra information is returned
+ *
+ * @param extra extra information
+ * @param context analysis context
+ */
+ @Override
+ public void extra(CellExtra extra, AnalysisContext context) {}
+
+ @Override
+ public boolean hasNext(AnalysisContext context) {
+ return true;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AnalysisEventListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AnalysisEventListener.java
new file mode 100644
index 0000000..015c7d3
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/AnalysisEventListener.java
@@ -0,0 +1,30 @@
+package ai.chat2db.excel.event;
+
+import java.util.Map;
+
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.read.listener.ReadListener;
+import ai.chat2db.excel.util.ConverterUtils;
+
+/**
+ * Receives the return of each piece of data parsed
+ *
+ * @author jipengfei
+ */
+public abstract class AnalysisEventListener implements ReadListener {
+
+ @Override
+ public void invokeHead(Map> headMap, AnalysisContext context) {
+ invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
+ }
+
+ /**
+ * Returns the header as a map.Override the current method to receive header data.
+ *
+ * @param headMap
+ * @param context
+ */
+ public void invokeHeadMap(Map headMap, AnalysisContext context) {}
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Handler.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Handler.java
new file mode 100644
index 0000000..1e0049e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Handler.java
@@ -0,0 +1,21 @@
+package ai.chat2db.excel.event;
+
+import ai.chat2db.excel.constant.OrderConstant;
+
+/**
+ * Intercepts handle some business logic
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface Handler extends Order {
+
+ /**
+ * handler order
+ *
+ * @return order
+ */
+ @Override
+ default int order() {
+ return OrderConstant.DEFAULT_ORDER;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Listener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Listener.java
new file mode 100644
index 0000000..34be780
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Listener.java
@@ -0,0 +1,8 @@
+package ai.chat2db.excel.event;
+
+/**
+ * Interface to listen for processing results
+ *
+ * @author Jiaju Zhuang
+ */
+public interface Listener {}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/NotRepeatExecutor.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/NotRepeatExecutor.java
new file mode 100644
index 0000000..9b8523e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/NotRepeatExecutor.java
@@ -0,0 +1,16 @@
+package ai.chat2db.excel.event;
+
+/**
+ * There are multiple interceptors that execute only one of them when fired once.If you want to control which one to
+ * execute please use {@link Order}
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface NotRepeatExecutor {
+ /**
+ * To see if it's the same executor
+ *
+ * @return
+ */
+ String uniqueValue();
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Order.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Order.java
new file mode 100644
index 0000000..2b6d576
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/Order.java
@@ -0,0 +1,15 @@
+package ai.chat2db.excel.event;
+
+/**
+ * Implement this interface when sorting
+ *
+ * @author Jiaju Zhuang
+ */
+public interface Order {
+ /**
+ * The smaller the first implementation
+ *
+ * @return
+ */
+ int order();
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/SyncReadListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/SyncReadListener.java
new file mode 100644
index 0000000..0f73f2a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/event/SyncReadListener.java
@@ -0,0 +1,31 @@
+package ai.chat2db.excel.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ai.chat2db.excel.context.AnalysisContext;
+
+/**
+ * Synchronous data reading
+ *
+ * @author Jiaju Zhuang
+ */
+public class SyncReadListener extends AnalysisEventListener {
+ private List list = new ArrayList();
+
+ @Override
+ public void invoke(Object object, AnalysisContext context) {
+ list.add(object);
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {}
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisException.java
new file mode 100644
index 0000000..e66728b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisException.java
@@ -0,0 +1,22 @@
+package ai.chat2db.excel.exception;
+
+/**
+ *
+ * @author jipengfei
+ */
+public class ExcelAnalysisException extends ExcelRuntimeException {
+
+ public ExcelAnalysisException() {}
+
+ public ExcelAnalysisException(String message) {
+ super(message);
+ }
+
+ public ExcelAnalysisException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelAnalysisException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopException.java
new file mode 100644
index 0000000..00b3fac
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopException.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.exception;
+
+/**
+ * Throw the exception when you need to stop
+ * This exception will stop the entire excel parsing. If you only want to stop the parsing of a certain sheet, please
+ * use ExcelAnalysisStopSheetException.
+ *
+ * @author Jiaju Zhuang
+ * @see ExcelAnalysisStopException
+ */
+public class ExcelAnalysisStopException extends ExcelAnalysisException {
+
+ public ExcelAnalysisStopException() {}
+
+ public ExcelAnalysisStopException(String message) {
+ super(message);
+ }
+
+ public ExcelAnalysisStopException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelAnalysisStopException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopSheetException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopSheetException.java
new file mode 100644
index 0000000..ab0469c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelAnalysisStopSheetException.java
@@ -0,0 +1,30 @@
+package ai.chat2db.excel.exception;
+
+/**
+ * Throw the exception when you need to stop
+ * This exception will only stop the parsing of the current sheet. If you want to stop the entire excel parsing, please
+ * use ExcelAnalysisStopException.
+ *
+ * The com.alibaba.excel.read.listener.ReadListener#doAfterAllAnalysed(com.alibaba.excel.context.AnalysisContext) method
+ * is called after the call is stopped.
+ *
+ * @author Jiaju Zhuang
+ * @see ExcelAnalysisStopException
+ * @since 3.3.4
+ */
+public class ExcelAnalysisStopSheetException extends ExcelAnalysisException {
+
+ public ExcelAnalysisStopSheetException() {}
+
+ public ExcelAnalysisStopSheetException(String message) {
+ super(message);
+ }
+
+ public ExcelAnalysisStopSheetException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelAnalysisStopSheetException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelCommonException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelCommonException.java
new file mode 100644
index 0000000..f521833
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelCommonException.java
@@ -0,0 +1,22 @@
+package ai.chat2db.excel.exception;
+
+/**
+ *
+ * @author Jiaju Zhuang
+ */
+public class ExcelCommonException extends ExcelRuntimeException {
+
+ public ExcelCommonException() {}
+
+ public ExcelCommonException(String message) {
+ super(message);
+ }
+
+ public ExcelCommonException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelCommonException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelDataConvertException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelDataConvertException.java
new file mode 100644
index 0000000..82624a9
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelDataConvertException.java
@@ -0,0 +1,57 @@
+package ai.chat2db.excel.exception;
+
+import ai.chat2db.excel.write.builder.ExcelWriterBuilder;
+import ai.chat2db.excel.metadata.data.CellData;
+import ai.chat2db.excel.metadata.property.ExcelContentProperty;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data convert exception
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ExcelDataConvertException extends ExcelRuntimeException {
+ /**
+ * NotNull.
+ */
+ private Integer rowIndex;
+ /**
+ * NotNull.
+ */
+ private Integer columnIndex;
+ /**
+ * NotNull.
+ */
+ private CellData> cellData;
+ /**
+ * Nullable.Only when the header is configured and when the class header is used is not null.
+ *
+ * @see ExcelWriterBuilder#head(Class)
+ */
+ private ExcelContentProperty excelContentProperty;
+
+ public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData> cellData,
+ ExcelContentProperty excelContentProperty, String message) {
+ super(message);
+ this.rowIndex = rowIndex;
+ this.columnIndex = columnIndex;
+ this.cellData = cellData;
+ this.excelContentProperty = excelContentProperty;
+ }
+
+ public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData> cellData,
+ ExcelContentProperty excelContentProperty, String message, Throwable cause) {
+ super(message, cause);
+ this.rowIndex = rowIndex;
+ this.columnIndex = columnIndex;
+ this.cellData = cellData;
+ this.excelContentProperty = excelContentProperty;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelGenerateException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelGenerateException.java
new file mode 100644
index 0000000..89adc02
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelGenerateException.java
@@ -0,0 +1,19 @@
+package ai.chat2db.excel.exception;
+
+/**
+ * @author jipengfei
+ */
+public class ExcelGenerateException extends ExcelRuntimeException {
+
+ public ExcelGenerateException(String message) {
+ super(message);
+ }
+
+ public ExcelGenerateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelGenerateException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelRuntimeException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelRuntimeException.java
new file mode 100644
index 0000000..dd7d2ae
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelRuntimeException.java
@@ -0,0 +1,21 @@
+package ai.chat2db.excel.exception;
+
+/**
+ * Excel Exception
+ * @author Jiaju Zhuang
+ */
+public class ExcelRuntimeException extends RuntimeException {
+ public ExcelRuntimeException() {}
+
+ public ExcelRuntimeException(String message) {
+ super(message);
+ }
+
+ public ExcelRuntimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ExcelRuntimeException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelWriteDataConvertException.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelWriteDataConvertException.java
new file mode 100644
index 0000000..cb0ad46
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/exception/ExcelWriteDataConvertException.java
@@ -0,0 +1,36 @@
+package ai.chat2db.excel.exception;
+
+import ai.chat2db.excel.write.handler.context.CellWriteHandlerContext;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data convert exception
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ExcelWriteDataConvertException extends ExcelDataConvertException {
+ /**
+ * handler.
+ */
+ private CellWriteHandlerContext cellWriteHandlerContext;
+
+ public ExcelWriteDataConvertException(CellWriteHandlerContext cellWriteHandlerContext, String message) {
+ super(cellWriteHandlerContext.getRowIndex(), cellWriteHandlerContext.getColumnIndex(),
+ cellWriteHandlerContext.getFirstCellData(), cellWriteHandlerContext.getExcelContentProperty(), message);
+ this.cellWriteHandlerContext = cellWriteHandlerContext;
+ }
+
+ public ExcelWriteDataConvertException(CellWriteHandlerContext cellWriteHandlerContext, String message,
+ Throwable cause) {
+ super(cellWriteHandlerContext.getRowIndex(), cellWriteHandlerContext.getColumnIndex(),
+ cellWriteHandlerContext.getFirstCellData(), cellWriteHandlerContext.getExcelContentProperty(), message,
+ cause);
+ this.cellWriteHandlerContext = cellWriteHandlerContext;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractCell.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractCell.java
new file mode 100644
index 0000000..f1a0a0b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractCell.java
@@ -0,0 +1,24 @@
+package ai.chat2db.excel.metadata;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * cell
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class AbstractCell implements Cell {
+ /**
+ * Row index
+ */
+ private Integer rowIndex;
+ /**
+ * Column index
+ */
+ private Integer columnIndex;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractHolder.java
new file mode 100644
index 0000000..a633400
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractHolder.java
@@ -0,0 +1,112 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.List;
+import java.util.Map;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.converters.ConverterKeyBuild;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Write/read holder
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public abstract class AbstractHolder implements ConfigurationHolder {
+ /**
+ * Record whether it's new or from cache
+ */
+ private Boolean newInitialization;
+ /**
+ * You can only choose one of the {@link AbstractHolder#head} and {@link AbstractHolder#clazz}
+ */
+ private List> head;
+ /**
+ * You can only choose one of the {@link AbstractHolder#head} and {@link AbstractHolder#clazz}
+ */
+ private Class> clazz;
+ /**
+ * Some global variables
+ */
+ private GlobalConfiguration globalConfiguration;
+ /**
+ *
+ * Read key:
+ *
+ * Write key:
+ */
+ private Map> converterMap;
+
+ public AbstractHolder(BasicParameter basicParameter, AbstractHolder prentAbstractHolder) {
+ this.newInitialization = Boolean.TRUE;
+ if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) {
+ this.head = prentAbstractHolder.getHead();
+ } else {
+ this.head = basicParameter.getHead();
+ }
+ if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) {
+ this.clazz = prentAbstractHolder.getClazz();
+ } else {
+ this.clazz = basicParameter.getClazz();
+ }
+ this.globalConfiguration = new GlobalConfiguration();
+ if (basicParameter.getAutoTrim() == null) {
+ if (prentAbstractHolder != null) {
+ globalConfiguration.setAutoTrim(prentAbstractHolder.getGlobalConfiguration().getAutoTrim());
+ }
+ } else {
+ globalConfiguration.setAutoTrim(basicParameter.getAutoTrim());
+ }
+
+ if (basicParameter.getUse1904windowing() == null) {
+ if (prentAbstractHolder != null) {
+ globalConfiguration.setUse1904windowing(
+ prentAbstractHolder.getGlobalConfiguration().getUse1904windowing());
+ }
+ } else {
+ globalConfiguration.setUse1904windowing(basicParameter.getUse1904windowing());
+ }
+
+ if (basicParameter.getLocale() == null) {
+ if (prentAbstractHolder != null) {
+ globalConfiguration.setLocale(prentAbstractHolder.getGlobalConfiguration().getLocale());
+ }
+ } else {
+ globalConfiguration.setLocale(basicParameter.getLocale());
+ }
+
+ if (basicParameter.getFiledCacheLocation() == null) {
+ if (prentAbstractHolder != null) {
+ globalConfiguration.setFiledCacheLocation(
+ prentAbstractHolder.getGlobalConfiguration().getFiledCacheLocation());
+ }
+ } else {
+ globalConfiguration.setFiledCacheLocation(basicParameter.getFiledCacheLocation());
+ }
+
+ }
+
+ @Override
+ public Map> converterMap() {
+ return getConverterMap();
+ }
+
+ @Override
+ public GlobalConfiguration globalConfiguration() {
+ return getGlobalConfiguration();
+ }
+
+ @Override
+ public boolean isNew() {
+ return getNewInitialization();
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractParameterBuilder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractParameterBuilder.java
new file mode 100644
index 0000000..a970489
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/AbstractParameterBuilder.java
@@ -0,0 +1,111 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.List;
+import java.util.Locale;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CacheLocationEnum;
+import ai.chat2db.excel.util.ListUtils;
+
+/**
+ * ExcelBuilder
+ *
+ * @author Jiaju Zhuang
+ */
+public abstract class AbstractParameterBuilder {
+ /**
+ * You can only choose one of the {@link #head(List)} and {@link #head(Class)}
+ *
+ * @param head
+ * @return
+ */
+ public T head(List> head) {
+ parameter().setHead(head);
+ return self();
+ }
+
+ /**
+ * You can only choose one of the {@link #head(List)} and {@link #head(Class)}
+ *
+ * @param clazz
+ * @return
+ */
+ public T head(Class> clazz) {
+ parameter().setClazz(clazz);
+ return self();
+ }
+
+ /**
+ * Custom type conversions override the default.
+ *
+ * @param converter
+ * @return
+ */
+ public T registerConverter(Converter> converter) {
+ if (parameter().getCustomConverterList() == null) {
+ parameter().setCustomConverterList(ListUtils.newArrayList());
+ }
+ parameter().getCustomConverterList().add(converter);
+ return self();
+ }
+
+ /**
+ * true if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * default is false
+ *
+ * @param use1904windowing
+ * @return
+ */
+ public T use1904windowing(Boolean use1904windowing) {
+ parameter().setUse1904windowing(use1904windowing);
+ return self();
+ }
+
+ /**
+ * A Locale
object represents a specific geographical, political, or cultural region. This parameter is
+ * used when formatting dates and numbers.
+ *
+ * @param locale
+ * @return
+ */
+ public T locale(Locale locale) {
+ parameter().setLocale(locale);
+ return self();
+ }
+
+ /**
+ * The cache used when parsing fields such as head.
+ *
+ * default is THREAD_LOCAL.
+ *
+ * @since 3.3.0
+ */
+ public T filedCacheLocation(CacheLocationEnum filedCacheLocation) {
+ parameter().setFiledCacheLocation(filedCacheLocation);
+ return self();
+ }
+
+ /**
+ * Automatic trim includes sheet name and content
+ *
+ * @param autoTrim
+ * @return
+ */
+ public T autoTrim(Boolean autoTrim) {
+ parameter().setAutoTrim(autoTrim);
+ return self();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T self() {
+ return (T)this;
+ }
+
+ /**
+ * Get parameter
+ *
+ * @return
+ */
+ protected abstract C parameter();
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/BasicParameter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/BasicParameter.java
new file mode 100644
index 0000000..105d48d
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/BasicParameter.java
@@ -0,0 +1,65 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.List;
+import java.util.Locale;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.enums.CacheLocationEnum;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Basic parameter
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class BasicParameter {
+ /**
+ * You can only choose one of the {@link BasicParameter#head} and {@link BasicParameter#clazz}
+ */
+ private List> head;
+ /**
+ * You can only choose one of the {@link BasicParameter#head} and {@link BasicParameter#clazz}
+ */
+ private Class> clazz;
+ /**
+ * Custom type conversions override the default
+ */
+ private List> customConverterList;
+ /**
+ * Automatic trim includes sheet name and content
+ */
+ private Boolean autoTrim;
+ /**
+ * true if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * default is false
+ *
+ * @return
+ */
+ private Boolean use1904windowing;
+ /**
+ * A Locale
object represents a specific geographical, political, or cultural region. This parameter is
+ * used when formatting dates and numbers.
+ */
+ private Locale locale;
+
+ /**
+ * Whether to use scientific Format.
+ *
+ * default is false
+ */
+ private Boolean useScientificFormat;
+
+ /**
+ * The cache used when parsing fields such as head.
+ *
+ * default is THREAD_LOCAL.
+ */
+ private CacheLocationEnum filedCacheLocation;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Cell.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Cell.java
new file mode 100644
index 0000000..dc1a64b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Cell.java
@@ -0,0 +1,22 @@
+package ai.chat2db.excel.metadata;
+
+/**
+ * Cell
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface Cell {
+ /**
+ * Row index
+ *
+ * @return
+ */
+ Integer getRowIndex();
+
+ /**
+ * Column index
+ *
+ * @return
+ */
+ Integer getColumnIndex();
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellExtra.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellExtra.java
new file mode 100644
index 0000000..8adcc95
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellExtra.java
@@ -0,0 +1,121 @@
+package ai.chat2db.excel.metadata;
+
+import ai.chat2db.excel.enums.CellExtraTypeEnum;
+import org.apache.poi.ss.util.CellReference;
+
+import ai.chat2db.excel.constant.ExcelXmlConstants;
+
+/**
+ * Cell extra information.
+ *
+ * @author Jiaju Zhuang
+ */
+public class CellExtra extends AbstractCell {
+ /**
+ * Cell extra type
+ */
+ private CellExtraTypeEnum type;
+ /**
+ * Cell extra data
+ */
+ private String text;
+ /**
+ * First row index, if this object is an interval
+ */
+ private Integer firstRowIndex;
+ /**
+ * Last row index, if this object is an interval
+ */
+ private Integer lastRowIndex;
+ /**
+ * First column index, if this object is an interval
+ */
+ private Integer firstColumnIndex;
+ /**
+ * Last column index, if this object is an interval
+ */
+ private Integer lastColumnIndex;
+
+ public CellExtra(CellExtraTypeEnum type, String text, String range) {
+ super();
+ this.type = type;
+ this.text = text;
+ String[] ranges = range.split(ExcelXmlConstants.CELL_RANGE_SPLIT);
+ CellReference first = new CellReference(ranges[0]);
+ CellReference last = first;
+ this.firstRowIndex = first.getRow();
+ this.firstColumnIndex = (int)first.getCol();
+ setRowIndex(this.firstRowIndex);
+ setColumnIndex(this.firstColumnIndex);
+ if (ranges.length > 1) {
+ last = new CellReference(ranges[1]);
+ }
+ this.lastRowIndex = last.getRow();
+ this.lastColumnIndex = (int)last.getCol();
+ }
+
+ public CellExtra(CellExtraTypeEnum type, String text, Integer rowIndex, Integer columnIndex) {
+ this(type, text, rowIndex, rowIndex, columnIndex, columnIndex);
+ }
+
+ public CellExtra(CellExtraTypeEnum type, String text, Integer firstRowIndex, Integer lastRowIndex,
+ Integer firstColumnIndex, Integer lastColumnIndex) {
+ super();
+ setRowIndex(firstRowIndex);
+ setColumnIndex(firstColumnIndex);
+ this.type = type;
+ this.text = text;
+ this.firstRowIndex = firstRowIndex;
+ this.firstColumnIndex = firstColumnIndex;
+ this.lastRowIndex = lastRowIndex;
+ this.lastColumnIndex = lastColumnIndex;
+ }
+
+ public CellExtraTypeEnum getType() {
+ return type;
+ }
+
+ public void setType(CellExtraTypeEnum type) {
+ this.type = type;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Integer getFirstRowIndex() {
+ return firstRowIndex;
+ }
+
+ public void setFirstRowIndex(Integer firstRowIndex) {
+ this.firstRowIndex = firstRowIndex;
+ }
+
+ public Integer getFirstColumnIndex() {
+ return firstColumnIndex;
+ }
+
+ public void setFirstColumnIndex(Integer firstColumnIndex) {
+ this.firstColumnIndex = firstColumnIndex;
+ }
+
+ public Integer getLastRowIndex() {
+ return lastRowIndex;
+ }
+
+ public void setLastRowIndex(Integer lastRowIndex) {
+ this.lastRowIndex = lastRowIndex;
+ }
+
+ public Integer getLastColumnIndex() {
+ return lastColumnIndex;
+ }
+
+ public void setLastColumnIndex(Integer lastColumnIndex) {
+ this.lastColumnIndex = lastColumnIndex;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellRange.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellRange.java
new file mode 100644
index 0000000..027c79b
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/CellRange.java
@@ -0,0 +1,26 @@
+package ai.chat2db.excel.metadata;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author jipengfei
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CellRange {
+
+ private int firstRow;
+ private int lastRow;
+ private int firstCol;
+ private int lastCol;
+
+ public CellRange(int firstRow, int lastRow, int firstCol, int lastCol) {
+ this.firstRow = firstRow;
+ this.lastRow = lastRow;
+ this.firstCol = firstCol;
+ this.lastCol = lastCol;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/ConfigurationHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/ConfigurationHolder.java
new file mode 100644
index 0000000..bcb54ac
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/ConfigurationHolder.java
@@ -0,0 +1,35 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.Map;
+
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.converters.ConverterKeyBuild;
+
+/**
+ * Get the corresponding holder
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface ConfigurationHolder extends Holder {
+
+ /**
+ * Record whether it's new or from cache
+ *
+ * @return Record whether it's new or from cache
+ */
+ boolean isNew();
+
+ /**
+ * Some global variables
+ *
+ * @return Global configuration
+ */
+ GlobalConfiguration globalConfiguration();
+
+ /**
+ * What converter does the currently operated cell need to execute
+ *
+ * @return Converter
+ */
+ Map> converterMap();
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldCache.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldCache.java
new file mode 100644
index 0000000..0aa6c5e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldCache.java
@@ -0,0 +1,31 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.Map;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * filed cache
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@AllArgsConstructor
+public class FieldCache {
+
+ /**
+ * A field cache that has been sorted by a class.
+ * It will exclude fields that are not needed.
+ */
+ private Map sortedFieldMap;
+
+ /**
+ * Fields using the index attribute
+ */
+ private Map indexFieldMap;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldWrapper.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldWrapper.java
new file mode 100644
index 0000000..89f7707
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/FieldWrapper.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.metadata;
+
+import java.lang.reflect.Field;
+
+import ai.chat2db.excel.annotation.ExcelProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * filed wrapper
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@AllArgsConstructor
+@NoArgsConstructor
+public class FieldWrapper {
+
+ /**
+ * field
+ */
+ private Field field;
+
+ /**
+ * The field name matching cglib
+ */
+ private String fieldName;
+
+ /**
+ * The name of the sheet header.
+ *
+ * @see ExcelProperty
+ */
+ private String[] heads;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Font.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Font.java
new file mode 100644
index 0000000..86f8acb
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Font.java
@@ -0,0 +1,48 @@
+package ai.chat2db.excel.metadata;
+
+import ai.chat2db.excel.write.metadata.style.WriteFont;
+
+/**
+ *
+ * @author jipengfei
+ * @deprecated please use {@link WriteFont}
+ */
+@Deprecated
+public class Font {
+
+ /**
+ */
+ private String fontName;
+
+ /**
+ */
+ private short fontHeightInPoints;
+
+ /**
+ */
+ private boolean bold;
+
+ public String getFontName() {
+ return fontName;
+ }
+
+ public void setFontName(String fontName) {
+ this.fontName = fontName;
+ }
+
+ public short getFontHeightInPoints() {
+ return fontHeightInPoints;
+ }
+
+ public void setFontHeightInPoints(short fontHeightInPoints) {
+ this.fontHeightInPoints = fontHeightInPoints;
+ }
+
+ public boolean isBold() {
+ return bold;
+ }
+
+ public void setBold(boolean bold) {
+ this.bold = bold;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/GlobalConfiguration.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/GlobalConfiguration.java
new file mode 100644
index 0000000..ab86fe6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/GlobalConfiguration.java
@@ -0,0 +1,59 @@
+package ai.chat2db.excel.metadata;
+
+import java.util.Locale;
+
+import ai.chat2db.excel.enums.CacheLocationEnum;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Global configuration
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class GlobalConfiguration {
+ /**
+ * Automatic trim includes sheet name and content
+ */
+ private Boolean autoTrim;
+ /**
+ * true if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * default is false
+ *
+ * @return
+ */
+ private Boolean use1904windowing;
+ /**
+ * A Locale
object represents a specific geographical, political, or cultural region. This parameter is
+ * used when formatting dates and numbers.
+ */
+ private Locale locale;
+
+ /**
+ * Whether to use scientific Format.
+ *
+ * default is false
+ */
+ private Boolean useScientificFormat;
+
+ /**
+ * The cache used when parsing fields such as head.
+ *
+ * default is THREAD_LOCAL.
+ */
+ private CacheLocationEnum filedCacheLocation;
+
+ public GlobalConfiguration() {
+ this.autoTrim = Boolean.TRUE;
+ this.use1904windowing = Boolean.FALSE;
+ this.locale = Locale.getDefault();
+ this.useScientificFormat = Boolean.FALSE;
+ this.filedCacheLocation = CacheLocationEnum.THREAD_LOCAL;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Head.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Head.java
new file mode 100644
index 0000000..cdfeed2
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Head.java
@@ -0,0 +1,87 @@
+package ai.chat2db.excel.metadata;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import ai.chat2db.excel.exception.ExcelGenerateException;
+import ai.chat2db.excel.metadata.property.ColumnWidthProperty;
+import ai.chat2db.excel.metadata.property.FontProperty;
+import ai.chat2db.excel.metadata.property.LoopMergeProperty;
+import ai.chat2db.excel.metadata.property.StyleProperty;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * excel head
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class Head {
+ /**
+ * Column index of head
+ */
+ private Integer columnIndex;
+ /**
+ * It only has values when passed in {@link Sheet#setClazz(Class)} and {@link Table#setClazz(Class)}
+ */
+ private Field field;
+ /**
+ * It only has values when passed in {@link Sheet#setClazz(Class)} and {@link Table#setClazz(Class)}
+ */
+ private String fieldName;
+ /**
+ * Head name
+ */
+ private List headNameList;
+ /**
+ * Whether index is specified
+ */
+ private Boolean forceIndex;
+ /**
+ * Whether to specify a name
+ */
+ private Boolean forceName;
+
+ /**
+ * column with
+ */
+ private ColumnWidthProperty columnWidthProperty;
+
+ /**
+ * Loop merge
+ */
+ private LoopMergeProperty loopMergeProperty;
+ /**
+ * Head style
+ */
+ private StyleProperty headStyleProperty;
+ /**
+ * Head font
+ */
+ private FontProperty headFontProperty;
+
+ public Head(Integer columnIndex, Field field, String fieldName, List headNameList, Boolean forceIndex,
+ Boolean forceName) {
+ this.columnIndex = columnIndex;
+ this.field = field;
+ this.fieldName = fieldName;
+ if (headNameList == null) {
+ this.headNameList = new ArrayList<>();
+ } else {
+ this.headNameList = headNameList;
+ for (String headName : headNameList) {
+ if (headName == null) {
+ throw new ExcelGenerateException("head name can not be null.");
+ }
+ }
+ }
+ this.forceIndex = forceIndex;
+ this.forceName = forceName;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Holder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Holder.java
new file mode 100644
index 0000000..33b4ab5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/Holder.java
@@ -0,0 +1,20 @@
+package ai.chat2db.excel.metadata;
+
+import ai.chat2db.excel.enums.HolderEnum;
+
+/**
+ *
+ * Get the corresponding holder
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface Holder {
+
+ /**
+ * What holder is the return
+ *
+ * @return Holder enum.
+ */
+ HolderEnum holderType();
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/NullObject.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/NullObject.java
new file mode 100644
index 0000000..6aa39dd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/NullObject.java
@@ -0,0 +1,9 @@
+package ai.chat2db.excel.metadata;
+
+/**
+ * Null object.
+ *
+ * @author Jiaju Zhuang
+ */
+public class NullObject {
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCell.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCell.java
new file mode 100644
index 0000000..196603f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCell.java
@@ -0,0 +1,351 @@
+package ai.chat2db.excel.metadata.csv;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Calendar;
+import java.util.Date;
+
+import ai.chat2db.excel.enums.NumericCellTypeEnum;
+import ai.chat2db.excel.metadata.data.FormulaData;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.ss.SpreadsheetVersion;
+import org.apache.poi.ss.usermodel.CellBase;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.Hyperlink;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * csv cell
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CsvCell extends CellBase {
+
+ /**
+ * column index
+ */
+ @Getter(value = AccessLevel.NONE)
+ @Setter(value = AccessLevel.NONE)
+ private Integer columnIndex;
+
+ /**
+ * cell type
+ */
+ @Getter(value = AccessLevel.NONE)
+ @Setter(value = AccessLevel.NONE)
+ private CellType cellType;
+
+ /**
+ * numeric cell type
+ */
+ private NumericCellTypeEnum numericCellType;
+
+ /**
+ * workbook
+ */
+ private final CsvWorkbook csvWorkbook;
+
+ /**
+ * sheet
+ */
+ private final CsvSheet csvSheet;
+
+ /**
+ * row
+ */
+ private final CsvRow csvRow;
+
+ /**
+ * {@link CellType#NUMERIC}
+ */
+ private BigDecimal numberValue;
+ /**
+ * {@link CellType#STRING} and {@link CellType#ERROR} {@link CellType#FORMULA}
+ */
+ private String stringValue;
+ /**
+ * {@link CellType#BOOLEAN}
+ */
+ private Boolean booleanValue;
+
+ /**
+ * {@link CellType#NUMERIC}
+ */
+ private LocalDateTime dateValue;
+
+ /**
+ * formula
+ */
+ private FormulaData formulaData;
+
+ /**
+ * rich text string
+ */
+ private RichTextString richTextString;
+
+ /**
+ * style
+ */
+ private CellStyle cellStyle;
+
+ public CsvCell(CsvWorkbook csvWorkbook, CsvSheet csvSheet, CsvRow csvRow, Integer columnIndex, CellType cellType) {
+ this.csvWorkbook = csvWorkbook;
+ this.csvSheet = csvSheet;
+ this.csvRow = csvRow;
+ this.columnIndex = columnIndex;
+ this.cellType = cellType;
+ if (this.cellType == null) {
+ this.cellType = CellType._NONE;
+ }
+ }
+
+ @Override
+ protected void setCellTypeImpl(CellType cellType) {
+ this.cellType = cellType;
+ }
+
+ @Override
+ protected void setCellFormulaImpl(String formula) {
+ FormulaData formulaData = new FormulaData();
+ formulaData.setFormulaValue(formula);
+ this.formulaData = formulaData;
+ this.cellType = CellType.FORMULA;
+ }
+
+ @Override
+ protected void removeFormulaImpl() {
+ this.formulaData = null;
+ }
+
+ @Override
+ protected void setCellValueImpl(double value) {
+ numberValue = BigDecimal.valueOf(value);
+ this.cellType = CellType.NUMERIC;
+ }
+
+ @Override
+ protected void setCellValueImpl(Date value) {
+ if (value == null) {
+ return;
+ }
+ this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault());
+ this.cellType = CellType.NUMERIC;
+ this.numericCellType = NumericCellTypeEnum.DATE;
+ }
+
+ @Override
+ protected void setCellValueImpl(LocalDateTime value) {
+ this.dateValue = value;
+ this.cellType = CellType.NUMERIC;
+ this.numericCellType = NumericCellTypeEnum.DATE;
+ }
+
+ @Override
+ protected void setCellValueImpl(Calendar value) {
+ if (value == null) {
+ return;
+ }
+ this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault());
+ this.cellType = CellType.NUMERIC;
+ }
+
+ @Override
+ protected void setCellValueImpl(String value) {
+ this.stringValue = value;
+ this.cellType = CellType.STRING;
+ }
+
+ @Override
+ protected void setCellValueImpl(RichTextString value) {
+ richTextString = value;
+ this.cellType = CellType.STRING;
+ }
+
+ @Override
+ public void setCellValue(String value) {
+ if (value == null) {
+ setBlank();
+ return;
+ }
+ setCellValueImpl(value);
+ }
+
+ @Override
+ public void setCellValue(RichTextString value) {
+ if (value == null || value.getString() == null) {
+ setBlank();
+ return;
+ }
+ setCellValueImpl(value);
+ }
+
+ @Override
+ protected SpreadsheetVersion getSpreadsheetVersion() {
+ return null;
+ }
+
+ @Override
+ public int getColumnIndex() {
+ return columnIndex;
+ }
+
+ @Override
+ public int getRowIndex() {
+ return csvRow.getRowNum();
+ }
+
+ @Override
+ public Sheet getSheet() {
+ return csvRow.getSheet();
+ }
+
+ @Override
+ public Row getRow() {
+ return csvRow;
+ }
+
+ @Override
+ public CellType getCellType() {
+ return cellType;
+ }
+
+ @Override
+ public CellType getCachedFormulaResultType() {
+ return getCellType();
+ }
+
+ @Override
+ public String getCellFormula() {
+ if (formulaData == null) {
+ return null;
+ }
+ return formulaData.getFormulaValue();
+ }
+
+ @Override
+ public double getNumericCellValue() {
+ if (numberValue == null) {
+ return 0;
+ }
+ return numberValue.doubleValue();
+ }
+
+ @Override
+ public Date getDateCellValue() {
+ if (dateValue == null) {
+ return null;
+ }
+ return Date.from(dateValue.atZone(ZoneId.systemDefault()).toInstant());
+ }
+
+ @Override
+ public LocalDateTime getLocalDateTimeCellValue() {
+ return dateValue;
+ }
+
+ @Override
+ public RichTextString getRichStringCellValue() {
+ return richTextString;
+ }
+
+ @Override
+ public String getStringCellValue() {
+ return stringValue;
+ }
+
+ @Override
+ public void setCellValue(boolean value) {
+ this.booleanValue = value;
+ this.cellType = CellType.BOOLEAN;
+ }
+
+ @Override
+ public void setCellErrorValue(byte value) {
+ this.numberValue = BigDecimal.valueOf(value);
+ this.cellType = CellType.ERROR;
+ }
+
+ @Override
+ public boolean getBooleanCellValue() {
+ if (booleanValue == null) {
+ return false;
+ }
+ return booleanValue;
+ }
+
+ @Override
+ public byte getErrorCellValue() {
+ if (numberValue == null) {
+ return 0;
+ }
+ return numberValue.byteValue();
+ }
+
+ @Override
+ public void setCellStyle(CellStyle style) {
+ this.cellStyle = style;
+ }
+
+ @Override
+ public CellStyle getCellStyle() {
+ return cellStyle;
+ }
+
+ @Override
+ public void setAsActiveCell() {
+
+ }
+
+ @Override
+ public void setCellComment(Comment comment) {
+
+ }
+
+ @Override
+ public Comment getCellComment() {
+ return null;
+ }
+
+ @Override
+ public void removeCellComment() {
+
+ }
+
+ @Override
+ public Hyperlink getHyperlink() {
+ return null;
+ }
+
+ @Override
+ public void setHyperlink(Hyperlink link) {
+
+ }
+
+ @Override
+ public void removeHyperlink() {
+
+ }
+
+ @Override
+ public CellRangeAddress getArrayFormulaRange() {
+ return null;
+ }
+
+ @Override
+ public boolean isPartOfArrayFormulaGroup() {
+ return false;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCellStyle.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCellStyle.java
new file mode 100644
index 0000000..e82e418
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvCellStyle.java
@@ -0,0 +1,312 @@
+package ai.chat2db.excel.metadata.csv;
+
+import ai.chat2db.excel.metadata.data.DataFormatData;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Color;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+
+/**
+ * csv cell style
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CsvCellStyle implements CellStyle {
+
+ /**
+ * data format
+ */
+ private DataFormatData dataFormatData;
+
+ /**
+ * index
+ */
+ private Short index;
+
+ public CsvCellStyle(Short index) {
+ this.index = index;
+ }
+
+ @Override
+ public short getIndex() {
+ return index;
+ }
+
+ @Override
+ public void setDataFormat(short fmt) {
+ initDataFormatData();
+ dataFormatData.setIndex(fmt);
+ }
+
+ private void initDataFormatData() {
+ if (dataFormatData == null) {
+ dataFormatData = new DataFormatData();
+ }
+ }
+
+ @Override
+ public short getDataFormat() {
+ if (dataFormatData == null) {
+ return 0;
+ }
+ return dataFormatData.getIndex();
+ }
+
+ @Override
+ public String getDataFormatString() {
+ if (dataFormatData == null) {
+ return null;
+ }
+ return dataFormatData.getFormat();
+ }
+
+ @Override
+ public void setFont(Font font) {
+
+ }
+
+ @Override
+ public int getFontIndex() {
+ return 0;
+ }
+
+ @Override
+ public int getFontIndexAsInt() {
+ return 0;
+ }
+
+ @Override
+ public void setHidden(boolean hidden) {
+
+ }
+
+ @Override
+ public boolean getHidden() {
+ return false;
+ }
+
+ @Override
+ public void setLocked(boolean locked) {
+
+ }
+
+ @Override
+ public boolean getLocked() {
+ return false;
+ }
+
+ @Override
+ public void setQuotePrefixed(boolean quotePrefix) {
+
+ }
+
+ @Override
+ public boolean getQuotePrefixed() {
+ return false;
+ }
+
+ @Override
+ public void setAlignment(HorizontalAlignment align) {
+
+ }
+
+ @Override
+ public HorizontalAlignment getAlignment() {
+ return null;
+ }
+
+ @Override
+ public void setWrapText(boolean wrapped) {
+
+ }
+
+ @Override
+ public boolean getWrapText() {
+ return false;
+ }
+
+ @Override
+ public void setVerticalAlignment(VerticalAlignment align) {
+
+ }
+
+ @Override
+ public VerticalAlignment getVerticalAlignment() {
+ return null;
+ }
+
+ @Override
+ public void setRotation(short rotation) {
+
+ }
+
+ @Override
+ public short getRotation() {
+ return 0;
+ }
+
+ @Override
+ public void setIndention(short indent) {
+
+ }
+
+ @Override
+ public short getIndention() {
+ return 0;
+ }
+
+ @Override
+ public void setBorderLeft(BorderStyle border) {
+
+ }
+
+ @Override
+ public BorderStyle getBorderLeft() {
+ return null;
+ }
+
+ @Override
+ public void setBorderRight(BorderStyle border) {
+
+ }
+
+ @Override
+ public BorderStyle getBorderRight() {
+ return null;
+ }
+
+ @Override
+ public void setBorderTop(BorderStyle border) {
+
+ }
+
+ @Override
+ public BorderStyle getBorderTop() {
+ return null;
+ }
+
+ @Override
+ public void setBorderBottom(BorderStyle border) {
+
+ }
+
+ @Override
+ public BorderStyle getBorderBottom() {
+ return null;
+ }
+
+ @Override
+ public void setLeftBorderColor(short color) {
+
+ }
+
+ @Override
+ public short getLeftBorderColor() {
+ return 0;
+ }
+
+ @Override
+ public void setRightBorderColor(short color) {
+
+ }
+
+ @Override
+ public short getRightBorderColor() {
+ return 0;
+ }
+
+ @Override
+ public void setTopBorderColor(short color) {
+
+ }
+
+ @Override
+ public short getTopBorderColor() {
+ return 0;
+ }
+
+ @Override
+ public void setBottomBorderColor(short color) {
+
+ }
+
+ @Override
+ public short getBottomBorderColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFillPattern(FillPatternType fp) {
+
+ }
+
+ @Override
+ public FillPatternType getFillPattern() {
+ return null;
+ }
+
+ @Override
+ public void setFillBackgroundColor(short bg) {
+
+ }
+
+ @Override
+ public void setFillBackgroundColor(Color color) {
+
+ }
+
+ @Override
+ public short getFillBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public Color getFillBackgroundColorColor() {
+ return null;
+ }
+
+ @Override
+ public void setFillForegroundColor(short bg) {
+
+ }
+
+ @Override
+ public void setFillForegroundColor(Color color) {
+
+ }
+
+ @Override
+ public short getFillForegroundColor() {
+ return 0;
+ }
+
+ @Override
+ public Color getFillForegroundColorColor() {
+ return null;
+ }
+
+ @Override
+ public void cloneStyleFrom(CellStyle source) {
+
+ }
+
+ @Override
+ public void setShrinkToFit(boolean shrinkToFit) {
+
+ }
+
+ @Override
+ public boolean getShrinkToFit() {
+ return false;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvDataFormat.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvDataFormat.java
new file mode 100644
index 0000000..050808c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvDataFormat.java
@@ -0,0 +1,66 @@
+package ai.chat2db.excel.metadata.csv;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.util.MapUtils;
+import ai.chat2db.excel.constant.BuiltinFormats;
+
+import org.apache.poi.ss.usermodel.DataFormat;
+
+/**
+ * format data
+ *
+ * @author Jiaju Zhuang
+ */
+public class CsvDataFormat implements DataFormat {
+ /**
+ * It is stored in both map and list for easy retrieval
+ */
+ private final Map formatMap;
+ private final List formatList;
+
+ /**
+ * Excel's built-in format conversion.
+ */
+ private final Map builtinFormatsMap;
+ private final String[] builtinFormats;
+
+ public CsvDataFormat(Locale locale) {
+ formatMap = MapUtils.newHashMap();
+ formatList = ListUtils.newArrayList();
+ builtinFormatsMap = BuiltinFormats.switchBuiltinFormatsMap(locale);
+ builtinFormats = BuiltinFormats.switchBuiltinFormats(locale);
+ }
+
+ @Override
+ public short getFormat(String format) {
+ Short index = builtinFormatsMap.get(format);
+ if (index != null) {
+ return index;
+ }
+ index = formatMap.get(format);
+ if (index != null) {
+ return index;
+ }
+ short indexPrimitive = (short)(formatList.size() + BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX);
+ index = indexPrimitive;
+ formatList.add(format);
+ formatMap.put(format, index);
+ return indexPrimitive;
+ }
+
+ @Override
+ public String getFormat(short index) {
+ if (index < BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX) {
+ return builtinFormats[index];
+ }
+ int actualIndex = index - BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX;
+ if (actualIndex < formatList.size()) {
+ return formatList.get(actualIndex);
+ }
+ return null;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRichTextString.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRichTextString.java
new file mode 100644
index 0000000..5027af6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRichTextString.java
@@ -0,0 +1,68 @@
+package ai.chat2db.excel.metadata.csv;
+
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.RichTextString;
+
+/**
+ * rich text string
+ *
+ * @author Jiaju Zhuang
+ */
+public class CsvRichTextString implements RichTextString {
+ /**
+ * string
+ */
+ private final String string;
+
+ public CsvRichTextString(String string) {
+ this.string = string;
+ }
+
+ @Override
+ public void applyFont(int startIndex, int endIndex, short fontIndex) {
+
+ }
+
+ @Override
+ public void applyFont(int startIndex, int endIndex, Font font) {
+
+ }
+
+ @Override
+ public void applyFont(Font font) {
+
+ }
+
+ @Override
+ public void clearFormatting() {
+
+ }
+
+ @Override
+ public String getString() {
+ return string;
+ }
+
+ @Override
+ public int length() {
+ if (string == null) {
+ return 0;
+ }
+ return string.length();
+ }
+
+ @Override
+ public int numFormattingRuns() {
+ return 0;
+ }
+
+ @Override
+ public int getIndexOfFormattingRun(int index) {
+ return 0;
+ }
+
+ @Override
+ public void applyFont(short fontIndex) {
+
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRow.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRow.java
new file mode 100644
index 0000000..1ae7b2f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvRow.java
@@ -0,0 +1,196 @@
+package ai.chat2db.excel.metadata.csv;
+
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+
+/**
+ * csv row
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CsvRow implements Row {
+
+ /**
+ * cell list
+ */
+ private final List cellList;
+
+ /**
+ * workbook
+ */
+ private final CsvWorkbook csvWorkbook;
+
+ /**
+ * sheet
+ */
+ private final CsvSheet csvSheet;
+
+ /**
+ * row index
+ */
+ private Integer rowIndex;
+
+ /**
+ * style
+ */
+ private CellStyle cellStyle;
+
+ public CsvRow(CsvWorkbook csvWorkbook, CsvSheet csvSheet, Integer rowIndex) {
+ cellList = Lists.newArrayList();
+ this.csvWorkbook = csvWorkbook;
+ this.csvSheet = csvSheet;
+ this.rowIndex = rowIndex;
+ }
+
+ @Override
+ public Cell createCell(int column) {
+ CsvCell cell = new CsvCell(csvWorkbook, csvSheet, this, column, null);
+ cellList.add(cell);
+ return cell;
+ }
+
+ @Override
+ public Cell createCell(int column, CellType type) {
+ CsvCell cell = new CsvCell(csvWorkbook, csvSheet, this, column, type);
+ cellList.add(cell);
+ return cell;
+ }
+
+ @Override
+ public void removeCell(Cell cell) {
+ cellList.remove(cell);
+ }
+
+ @Override
+ public void setRowNum(int rowNum) {
+ this.rowIndex = rowNum;
+ }
+
+ @Override
+ public int getRowNum() {
+ return rowIndex;
+ }
+
+ @Override
+ public Cell getCell(int cellnum) {
+ if (cellnum >= cellList.size()) {
+ return null;
+ }
+ return cellList.get(cellnum - 1);
+ }
+
+ @Override
+ public Cell getCell(int cellnum, MissingCellPolicy policy) {
+ return getCell(cellnum);
+ }
+
+ @Override
+ public short getFirstCellNum() {
+ if (CollectionUtils.isEmpty(cellList)) {
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public short getLastCellNum() {
+ if (CollectionUtils.isEmpty(cellList)) {
+ return -1;
+ }
+ return (short)cellList.size();
+ }
+
+ @Override
+ public int getPhysicalNumberOfCells() {
+ return getRowNum();
+ }
+
+ @Override
+ public void setHeight(short height) {
+
+ }
+
+ @Override
+ public void setZeroHeight(boolean zHeight) {
+
+ }
+
+ @Override
+ public boolean getZeroHeight() {
+ return false;
+ }
+
+ @Override
+ public void setHeightInPoints(float height) {
+
+ }
+
+ @Override
+ public short getHeight() {
+ return 0;
+ }
+
+ @Override
+ public float getHeightInPoints() {
+ return 0;
+ }
+
+ @Override
+ public boolean isFormatted() {
+ return false;
+ }
+
+ @Override
+ public CellStyle getRowStyle() {
+ return cellStyle;
+ }
+
+ @Override
+ public void setRowStyle(CellStyle style) {
+ this.cellStyle = style;
+ }
+
+ @Override
+ public Iterator cellIterator() {
+ return (Iterator)(Iterator extends Cell>)cellList.iterator();
+ }
+
+ @Override
+ public Sheet getSheet() {
+ return csvSheet;
+ }
+
+ @Override
+ public int getOutlineLevel() {
+ return 0;
+ }
+
+ @Override
+ public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) {
+
+ }
+
+ @Override
+ public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) {
+
+ }
+
+ @Override
+ public Iterator iterator() {
+ return cellIterator();
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvSheet.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvSheet.java
new file mode 100644
index 0000000..989654a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvSheet.java
@@ -0,0 +1,831 @@
+package ai.chat2db.excel.metadata.csv;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import ai.chat2db.excel.enums.ByteOrderMarkEnum;
+import ai.chat2db.excel.enums.NumericCellTypeEnum;
+import ai.chat2db.excel.exception.ExcelGenerateException;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.util.NumberDataFormatterUtils;
+import ai.chat2db.excel.util.StringUtils;
+import ai.chat2db.excel.constant.BuiltinFormats;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.apache.poi.ss.usermodel.AutoFilter;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellRange;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Footer;
+import org.apache.poi.ss.usermodel.Header;
+import org.apache.poi.ss.usermodel.Hyperlink;
+import org.apache.poi.ss.usermodel.PageMargin;
+import org.apache.poi.ss.usermodel.PaneType;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.SheetConditionalFormatting;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellAddress;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.PaneInformation;
+
+/**
+ * csv sheet
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CsvSheet implements Sheet, Closeable {
+
+ /**
+ * workbook
+ */
+ private CsvWorkbook csvWorkbook;
+ /**
+ * output
+ */
+ private Appendable out;
+ /**
+ * row cache
+ */
+ private Integer rowCacheCount;
+ /**
+ * format
+ */
+ public CSVFormat csvFormat;
+
+ /**
+ * last row index
+ */
+ private Integer lastRowIndex;
+
+ /**
+ * row cache
+ */
+ private List rowCache;
+ /**
+ * csv printer
+ */
+ private CSVPrinter csvPrinter;
+
+ public CsvSheet(CsvWorkbook csvWorkbook, Appendable out) {
+ this.csvWorkbook = csvWorkbook;
+ this.out = out;
+ this.rowCacheCount = 100;
+ this.csvFormat = CSVFormat.DEFAULT;
+ this.lastRowIndex = -1;
+ }
+
+ @Override
+ public Row createRow(int rownum) {
+ // Initialize the data when the row is first created
+ initSheet();
+
+ lastRowIndex++;
+ assert rownum == lastRowIndex : "csv create row must be in order.";
+ printData();
+ CsvRow csvRow = new CsvRow(csvWorkbook, this, rownum);
+ rowCache.add(csvRow);
+ return csvRow;
+ }
+
+ private void initSheet() {
+ if (csvPrinter != null) {
+ return;
+ }
+ rowCache = ListUtils.newArrayListWithExpectedSize(rowCacheCount);
+ try {
+ if (csvWorkbook.getWithBom()) {
+ ByteOrderMarkEnum byteOrderMark = ByteOrderMarkEnum.valueOfByCharsetName(
+ csvWorkbook.getCharset().name());
+ if (byteOrderMark != null) {
+ out.append(byteOrderMark.getStringPrefix());
+ }
+ }
+ csvPrinter = csvFormat.print(out);
+ } catch (IOException e) {
+ throw new ExcelGenerateException(e);
+ }
+ }
+
+ @Override
+ public void removeRow(Row row) {
+ throw new UnsupportedOperationException("csv cannot move row.");
+ }
+
+ @Override
+ public Row getRow(int rownum) {
+ int actualRowIndex = rownum - (lastRowIndex - rowCache.size()) - 1;
+ if (actualRowIndex < 0 || actualRowIndex > rowCache.size() - 1) {
+ throw new UnsupportedOperationException("The current data does not exist or has been flushed to disk\n.");
+ }
+ return rowCache.get(actualRowIndex);
+ }
+
+ @Override
+ public int getPhysicalNumberOfRows() {
+ return lastRowIndex - rowCache.size();
+ }
+
+ @Override
+ public int getFirstRowNum() {
+ if (lastRowIndex < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public int getLastRowNum() {
+ return lastRowIndex;
+ }
+
+ @Override
+ public void setColumnHidden(int columnIndex, boolean hidden) {
+
+ }
+
+ @Override
+ public boolean isColumnHidden(int columnIndex) {
+ return false;
+ }
+
+ @Override
+ public void setRightToLeft(boolean value) {
+
+ }
+
+ @Override
+ public boolean isRightToLeft() {
+ return false;
+ }
+
+ @Override
+ public void setColumnWidth(int columnIndex, int width) {
+
+ }
+
+ @Override
+ public int getColumnWidth(int columnIndex) {
+ return 0;
+ }
+
+ @Override
+ public float getColumnWidthInPixels(int columnIndex) {
+ return 0;
+ }
+
+ @Override
+ public void setDefaultColumnWidth(int width) {
+
+ }
+
+ @Override
+ public int getDefaultColumnWidth() {
+ return 0;
+ }
+
+ @Override
+ public short getDefaultRowHeight() {
+ return 0;
+ }
+
+ @Override
+ public float getDefaultRowHeightInPoints() {
+ return 0;
+ }
+
+ @Override
+ public void setDefaultRowHeight(short height) {
+
+ }
+
+ @Override
+ public void setDefaultRowHeightInPoints(float height) {
+
+ }
+
+ @Override
+ public CellStyle getColumnStyle(int column) {
+ return null;
+ }
+
+ @Override
+ public int addMergedRegion(CellRangeAddress region) {
+ return 0;
+ }
+
+ @Override
+ public int addMergedRegionUnsafe(CellRangeAddress region) {
+ return 0;
+ }
+
+ @Override
+ public void validateMergedRegions() {
+
+ }
+
+ @Override
+ public void setVerticallyCenter(boolean value) {
+
+ }
+
+ @Override
+ public void setHorizontallyCenter(boolean value) {
+
+ }
+
+ @Override
+ public boolean getHorizontallyCenter() {
+ return false;
+ }
+
+ @Override
+ public boolean getVerticallyCenter() {
+ return false;
+ }
+
+ @Override
+ public void removeMergedRegion(int index) {
+
+ }
+
+ @Override
+ public void removeMergedRegions(Collection indices) {
+
+ }
+
+ @Override
+ public int getNumMergedRegions() {
+ return 0;
+ }
+
+ @Override
+ public CellRangeAddress getMergedRegion(int index) {
+ return null;
+ }
+
+ @Override
+ public List getMergedRegions() {
+ return null;
+ }
+
+ @Override
+ public Iterator rowIterator() {
+ return (Iterator)(Iterator extends Row>)rowCache.iterator();
+ }
+
+ @Override
+ public void setForceFormulaRecalculation(boolean value) {
+
+ }
+
+ @Override
+ public boolean getForceFormulaRecalculation() {
+ return false;
+ }
+
+ @Override
+ public void setAutobreaks(boolean value) {
+
+ }
+
+ @Override
+ public void setDisplayGuts(boolean value) {
+
+ }
+
+ @Override
+ public void setDisplayZeros(boolean value) {
+
+ }
+
+ @Override
+ public boolean isDisplayZeros() {
+ return false;
+ }
+
+ @Override
+ public void setFitToPage(boolean value) {
+
+ }
+
+ @Override
+ public void setRowSumsBelow(boolean value) {
+
+ }
+
+ @Override
+ public void setRowSumsRight(boolean value) {
+
+ }
+
+ @Override
+ public boolean getAutobreaks() {
+ return false;
+ }
+
+ @Override
+ public boolean getDisplayGuts() {
+ return false;
+ }
+
+ @Override
+ public boolean getFitToPage() {
+ return false;
+ }
+
+ @Override
+ public boolean getRowSumsBelow() {
+ return false;
+ }
+
+ @Override
+ public boolean getRowSumsRight() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrintGridlines() {
+ return false;
+ }
+
+ @Override
+ public void setPrintGridlines(boolean show) {
+
+ }
+
+ @Override
+ public boolean isPrintRowAndColumnHeadings() {
+ return false;
+ }
+
+ @Override
+ public void setPrintRowAndColumnHeadings(boolean show) {
+
+ }
+
+ @Override
+ public PrintSetup getPrintSetup() {
+ return null;
+ }
+
+ @Override
+ public Header getHeader() {
+ return null;
+ }
+
+ @Override
+ public Footer getFooter() {
+ return null;
+ }
+
+ @Override
+ public void setSelected(boolean value) {
+
+ }
+
+ @Override
+ public double getMargin(short margin) {
+ return 0;
+ }
+
+ @Override
+ public double getMargin(PageMargin pageMargin) {
+ return 0;
+ }
+
+ @Override
+ public void setMargin(short margin, double size) {
+
+ }
+
+ @Override
+ public void setMargin(PageMargin pageMargin, double v) {
+
+ }
+
+ @Override
+ public boolean getProtect() {
+ return false;
+ }
+
+ @Override
+ public void protectSheet(String password) {
+
+ }
+
+ @Override
+ public boolean getScenarioProtect() {
+ return false;
+ }
+
+ @Override
+ public void setZoom(int scale) {
+
+ }
+
+ @Override
+ public short getTopRow() {
+ return 0;
+ }
+
+ @Override
+ public short getLeftCol() {
+ return 0;
+ }
+
+ @Override
+ public void showInPane(int topRow, int leftCol) {
+
+ }
+
+ @Override
+ public void shiftRows(int startRow, int endRow, int n) {
+
+ }
+
+ @Override
+ public void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight) {
+
+ }
+
+ @Override
+ public void shiftColumns(int startColumn, int endColumn, int n) {
+
+ }
+
+ @Override
+ public void createFreezePane(int colSplit, int rowSplit, int leftmostColumn, int topRow) {
+
+ }
+
+ @Override
+ public void createFreezePane(int colSplit, int rowSplit) {
+
+ }
+
+ @Override
+ public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane) {
+
+ }
+
+ @Override
+ public void createSplitPane(int i, int i1, int i2, int i3, PaneType paneType) {
+
+ }
+
+ @Override
+ public PaneInformation getPaneInformation() {
+ return null;
+ }
+
+ @Override
+ public void setDisplayGridlines(boolean show) {
+
+ }
+
+ @Override
+ public boolean isDisplayGridlines() {
+ return false;
+ }
+
+ @Override
+ public void setDisplayFormulas(boolean show) {
+
+ }
+
+ @Override
+ public boolean isDisplayFormulas() {
+ return false;
+ }
+
+ @Override
+ public void setDisplayRowColHeadings(boolean show) {
+
+ }
+
+ @Override
+ public boolean isDisplayRowColHeadings() {
+ return false;
+ }
+
+ @Override
+ public void setRowBreak(int row) {
+
+ }
+
+ @Override
+ public boolean isRowBroken(int row) {
+ return false;
+ }
+
+ @Override
+ public void removeRowBreak(int row) {
+
+ }
+
+ @Override
+ public int[] getRowBreaks() {
+ return new int[0];
+ }
+
+ @Override
+ public int[] getColumnBreaks() {
+ return new int[0];
+ }
+
+ @Override
+ public void setColumnBreak(int column) {
+
+ }
+
+ @Override
+ public boolean isColumnBroken(int column) {
+ return false;
+ }
+
+ @Override
+ public void removeColumnBreak(int column) {
+
+ }
+
+ @Override
+ public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
+
+ }
+
+ @Override
+ public void groupColumn(int fromColumn, int toColumn) {
+
+ }
+
+ @Override
+ public void ungroupColumn(int fromColumn, int toColumn) {
+
+ }
+
+ @Override
+ public void groupRow(int fromRow, int toRow) {
+
+ }
+
+ @Override
+ public void ungroupRow(int fromRow, int toRow) {
+
+ }
+
+ @Override
+ public void setRowGroupCollapsed(int row, boolean collapse) {
+
+ }
+
+ @Override
+ public void setDefaultColumnStyle(int column, CellStyle style) {
+
+ }
+
+ @Override
+ public void autoSizeColumn(int column) {
+
+ }
+
+ @Override
+ public void autoSizeColumn(int column, boolean useMergedCells) {
+
+ }
+
+ @Override
+ public Comment getCellComment(CellAddress ref) {
+ return null;
+ }
+
+ @Override
+ public Map getCellComments() {
+ return null;
+ }
+
+ @Override
+ public Drawing> getDrawingPatriarch() {
+ return null;
+ }
+
+ @Override
+ public Drawing> createDrawingPatriarch() {
+ return null;
+ }
+
+ @Override
+ public Workbook getWorkbook() {
+ return csvWorkbook;
+ }
+
+ @Override
+ public String getSheetName() {
+ return null;
+ }
+
+ @Override
+ public boolean isSelected() {
+ return false;
+ }
+
+ @Override
+ public CellRange extends Cell> setArrayFormula(
+ String formula, CellRangeAddress range) {
+ return null;
+ }
+
+ @Override
+ public CellRange extends Cell> removeArrayFormula(Cell cell) {
+ return null;
+ }
+
+ @Override
+ public DataValidationHelper getDataValidationHelper() {
+ return null;
+ }
+
+ @Override
+ public List extends DataValidation> getDataValidations() {
+ return null;
+ }
+
+ @Override
+ public void addValidationData(DataValidation dataValidation) {
+
+ }
+
+ @Override
+ public AutoFilter setAutoFilter(CellRangeAddress range) {
+ return null;
+ }
+
+ @Override
+ public SheetConditionalFormatting getSheetConditionalFormatting() {
+ return null;
+ }
+
+ @Override
+ public CellRangeAddress getRepeatingRows() {
+ return null;
+ }
+
+ @Override
+ public CellRangeAddress getRepeatingColumns() {
+ return null;
+ }
+
+ @Override
+ public void setRepeatingRows(CellRangeAddress rowRangeRef) {
+
+ }
+
+ @Override
+ public void setRepeatingColumns(CellRangeAddress columnRangeRef) {
+
+ }
+
+ @Override
+ public int getColumnOutlineLevel(int columnIndex) {
+ return 0;
+ }
+
+ @Override
+ public Hyperlink getHyperlink(int row, int column) {
+ return null;
+ }
+
+ @Override
+ public Hyperlink getHyperlink(CellAddress addr) {
+ return null;
+ }
+
+ @Override
+ public List extends Hyperlink> getHyperlinkList() {
+ return null;
+ }
+
+ @Override
+ public CellAddress getActiveCell() {
+ return null;
+ }
+
+ @Override
+ public void setActiveCell(CellAddress address) {
+
+ }
+
+ @Override
+ public Iterator iterator() {
+ return rowIterator();
+ }
+
+ @Override
+ public void close() throws IOException {
+ // Avoid empty sheets
+ initSheet();
+
+ flushData();
+ csvPrinter.flush();
+ csvPrinter.close();
+ }
+
+ public void printData() {
+ if (rowCache.size() >= rowCacheCount) {
+ flushData();
+ }
+ }
+
+ public void flushData() {
+ try {
+ for (CsvRow row : rowCache) {
+ Iterator cellIterator = row.cellIterator();
+ int columnIndex = 0;
+ while (cellIterator.hasNext()) {
+ CsvCell csvCell = (CsvCell)cellIterator.next();
+ while (csvCell.getColumnIndex() > columnIndex++) {
+ csvPrinter.print(null);
+ }
+ csvPrinter.print(buildCellValue(csvCell));
+ }
+ csvPrinter.println();
+ }
+ rowCache.clear();
+ } catch (IOException e) {
+ throw new ExcelGenerateException(e);
+ }
+ }
+
+ private String buildCellValue(CsvCell csvCell) {
+ switch (csvCell.getCellType()) {
+ case STRING:
+ case ERROR:
+ return csvCell.getStringCellValue();
+ case NUMERIC:
+ Short dataFormat = null;
+ String dataFormatString = null;
+ if (csvCell.getCellStyle() != null) {
+ dataFormat = csvCell.getCellStyle().getDataFormat();
+ dataFormatString = csvCell.getCellStyle().getDataFormatString();
+ }
+ if (csvCell.getNumericCellType() == NumericCellTypeEnum.DATE) {
+ if (csvCell.getDateValue() == null) {
+ return null;
+ }
+ // date
+ if (dataFormat == null) {
+ dataFormatString = DateUtils.defaultDateFormat;
+ dataFormat = csvWorkbook.createDataFormat().getFormat(dataFormatString);
+ }
+ if (dataFormatString == null) {
+ dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat);
+ }
+ return NumberDataFormatterUtils.format(BigDecimal.valueOf(
+ DateUtil.getExcelDate(csvCell.getDateValue(), csvWorkbook.getUse1904windowing())),
+ dataFormat, dataFormatString, csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(),
+ csvWorkbook.getUseScientificFormat());
+ } else {
+ if (csvCell.getNumberValue() == null) {
+ return null;
+ }
+ //number
+ if (dataFormat == null) {
+ dataFormat = BuiltinFormats.GENERAL;
+ dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat);
+ }
+ if (dataFormatString == null) {
+ dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat);
+ }
+ return NumberDataFormatterUtils.format(csvCell.getNumberValue(), dataFormat, dataFormatString,
+ csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(),
+ csvWorkbook.getUseScientificFormat());
+ }
+ case BOOLEAN:
+ return csvCell.getBooleanValue().toString();
+ case BLANK:
+ return StringUtils.EMPTY;
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvWorkbook.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvWorkbook.java
new file mode 100644
index 0000000..98d9349
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/csv/CsvWorkbook.java
@@ -0,0 +1,419 @@
+package ai.chat2db.excel.metadata.csv;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.poi.ss.SpreadsheetVersion;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
+import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.CellReferenceType;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.SheetVisibility;
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * csv workbook
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CsvWorkbook implements Workbook {
+ /**
+ * output
+ */
+ private Appendable out;
+
+ /**
+ * true if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * default is false
+ *
+ */
+ private Boolean use1904windowing;
+
+ /**
+ * locale
+ */
+ private Locale locale;
+
+ /**
+ * Whether to use scientific Format.
+ *
+ * default is false
+ */
+ private Boolean useScientificFormat;
+
+ /**
+ * data format
+ */
+ private CsvDataFormat csvDataFormat;
+ /**
+ * sheet
+ */
+ private CsvSheet csvSheet;
+ /**
+ * cell style
+ */
+ private List csvCellStyleList;
+
+ /**
+ * charset.
+ */
+ private Charset charset;
+
+ /**
+ * Set the encoding prefix in the csv file, otherwise the office may open garbled characters.
+ * Default true.
+ */
+ private Boolean withBom;
+
+ public CsvWorkbook(Appendable out, Locale locale, Boolean use1904windowing, Boolean useScientificFormat,
+ Charset charset, Boolean withBom) {
+ this.out = out;
+ this.locale = locale;
+ this.use1904windowing = use1904windowing;
+ this.useScientificFormat = useScientificFormat;
+ this.charset = charset;
+ this.withBom = withBom;
+ }
+
+ @Override
+ public int getActiveSheetIndex() {
+ return 0;
+ }
+
+ @Override
+ public void setActiveSheet(int sheetIndex) {
+
+ }
+
+ @Override
+ public int getFirstVisibleTab() {
+ return 0;
+ }
+
+ @Override
+ public void setFirstVisibleTab(int sheetIndex) {
+
+ }
+
+ @Override
+ public void setSheetOrder(String sheetname, int pos) {
+
+ }
+
+ @Override
+ public void setSelectedTab(int index) {
+
+ }
+
+ @Override
+ public void setSheetName(int sheet, String name) {
+
+ }
+
+ @Override
+ public String getSheetName(int sheet) {
+ return null;
+ }
+
+ @Override
+ public int getSheetIndex(String name) {
+ return 0;
+ }
+
+ @Override
+ public int getSheetIndex(Sheet sheet) {
+ return 0;
+ }
+
+ @Override
+ public Sheet createSheet() {
+ assert csvSheet == null : "CSV repeat creation is not allowed.";
+ csvSheet = new CsvSheet(this, out);
+ return csvSheet;
+ }
+
+ @Override
+ public Sheet createSheet(String sheetname) {
+ assert csvSheet == null : "CSV repeat creation is not allowed.";
+ csvSheet = new CsvSheet(this, out);
+ return csvSheet;
+ }
+
+ @Override
+ public Sheet cloneSheet(int sheetNum) {
+ return null;
+ }
+
+ @Override
+ public Iterator sheetIterator() {
+ return null;
+ }
+
+ @Override
+ public int getNumberOfSheets() {
+ return 0;
+ }
+
+ @Override
+ public Sheet getSheetAt(int index) {
+ assert index == 0 : "CSV exists only in one sheet.";
+ return csvSheet;
+ }
+
+ @Override
+ public Sheet getSheet(String name) {
+ return csvSheet;
+ }
+
+ @Override
+ public void removeSheetAt(int index) {
+
+ }
+
+ @Override
+ public Font createFont() {
+ return null;
+ }
+
+ @Override
+ public Font findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout,
+ short typeOffset, byte underline) {
+ return null;
+ }
+
+ @Override
+ public int getNumberOfFonts() {
+ return 0;
+ }
+
+ @Override
+ public int getNumberOfFontsAsInt() {
+ return 0;
+ }
+
+ @Override
+ public Font getFontAt(int idx) {
+ return null;
+ }
+
+ @Override
+ public CellStyle createCellStyle() {
+ if (csvCellStyleList == null) {
+ csvCellStyleList = Lists.newArrayList();
+ }
+ CsvCellStyle csvCellStyle = new CsvCellStyle((short)csvCellStyleList.size());
+ csvCellStyleList.add(csvCellStyle);
+ return csvCellStyle;
+ }
+
+ @Override
+ public int getNumCellStyles() {
+ return csvCellStyleList.size();
+ }
+
+ @Override
+ public CellStyle getCellStyleAt(int idx) {
+ if (idx < 0 || idx >= csvCellStyleList.size()) {
+ return null;
+ }
+ return csvCellStyleList.get(idx);
+ }
+
+ @Override
+ public void write(OutputStream stream) throws IOException {
+ csvSheet.close();
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+ @Override
+ public int getNumberOfNames() {
+ return 0;
+ }
+
+ @Override
+ public Name getName(String name) {
+ return null;
+ }
+
+ @Override
+ public List extends Name> getNames(String name) {
+ return null;
+ }
+
+ @Override
+ public List extends Name> getAllNames() {
+ return null;
+ }
+
+ @Override
+ public Name createName() {
+ return null;
+ }
+
+ @Override
+ public void removeName(Name name) {
+
+ }
+
+ @Override
+ public int linkExternalWorkbook(String name, Workbook workbook) {
+ return 0;
+ }
+
+ @Override
+ public void setPrintArea(int sheetIndex, String reference) {
+
+ }
+
+ @Override
+ public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) {
+
+ }
+
+ @Override
+ public String getPrintArea(int sheetIndex) {
+ return null;
+ }
+
+ @Override
+ public void removePrintArea(int sheetIndex) {
+
+ }
+
+ @Override
+ public MissingCellPolicy getMissingCellPolicy() {
+ return null;
+ }
+
+ @Override
+ public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
+
+ }
+
+ @Override
+ public DataFormat createDataFormat() {
+ if (csvDataFormat != null) {
+ return csvDataFormat;
+ }
+ csvDataFormat = new CsvDataFormat(locale);
+ return csvDataFormat;
+ }
+
+ @Override
+ public int addPicture(byte[] pictureData, int format) {
+ return 0;
+ }
+
+ @Override
+ public List extends PictureData> getAllPictures() {
+ return null;
+ }
+
+ @Override
+ public CreationHelper getCreationHelper() {
+ return null;
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public void setHidden(boolean hiddenFlag) {
+
+ }
+
+ @Override
+ public boolean isSheetHidden(int sheetIx) {
+ return false;
+ }
+
+ @Override
+ public boolean isSheetVeryHidden(int sheetIx) {
+ return false;
+ }
+
+ @Override
+ public void setSheetHidden(int sheetIx, boolean hidden) {
+
+ }
+
+ @Override
+ public SheetVisibility getSheetVisibility(int sheetIx) {
+ return null;
+ }
+
+ @Override
+ public void setSheetVisibility(int sheetIx, SheetVisibility visibility) {
+
+ }
+
+ @Override
+ public void addToolPack(UDFFinder toopack) {
+
+ }
+
+ @Override
+ public void setForceFormulaRecalculation(boolean value) {
+
+ }
+
+ @Override
+ public boolean getForceFormulaRecalculation() {
+ return false;
+ }
+
+ @Override
+ public SpreadsheetVersion getSpreadsheetVersion() {
+ return null;
+ }
+
+ @Override
+ public int addOlePackage(byte[] oleData, String label, String fileName, String command) {
+ return 0;
+ }
+
+ @Override
+ public EvaluationWorkbook createEvaluationWorkbook() {
+ return null;
+ }
+
+ @Override
+ public CellReferenceType getCellReferenceType() {
+ return null;
+ }
+
+ @Override
+ public void setCellReferenceType(CellReferenceType cellReferenceType) {
+
+ }
+
+ @Override
+ public Iterator iterator() {
+ return null;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CellData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CellData.java
new file mode 100644
index 0000000..b464279
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CellData.java
@@ -0,0 +1,80 @@
+package ai.chat2db.excel.metadata.data;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.metadata.AbstractCell;
+import ai.chat2db.excel.util.StringUtils;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Excel internal cell data.
+ *
+ *
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CellData extends AbstractCell {
+ /**
+ * cell type
+ */
+ private CellDataTypeEnum type;
+ /**
+ * {@link CellDataTypeEnum#NUMBER}
+ */
+ private BigDecimal numberValue;
+ /**
+ * {@link CellDataTypeEnum#STRING} and{@link CellDataTypeEnum#ERROR}
+ */
+ private String stringValue;
+ /**
+ * {@link CellDataTypeEnum#BOOLEAN}
+ */
+ private Boolean booleanValue;
+
+ /**
+ * The resulting converted data.
+ */
+ private T data;
+
+ /**
+ * formula
+ */
+ private FormulaData formulaData;
+
+ /**
+ * Ensure that the object does not appear null
+ */
+ public void checkEmpty() {
+ if (type == null) {
+ type = CellDataTypeEnum.EMPTY;
+ }
+ switch (type) {
+ case STRING:
+ case DIRECT_STRING:
+ case ERROR:
+ if (StringUtils.isEmpty(stringValue)) {
+ type = CellDataTypeEnum.EMPTY;
+ }
+ return;
+ case NUMBER:
+ if (numberValue == null) {
+ type = CellDataTypeEnum.EMPTY;
+ }
+ return;
+ case BOOLEAN:
+ if (booleanValue == null) {
+ type = CellDataTypeEnum.EMPTY;
+ }
+ return;
+ default:
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ClientAnchorData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ClientAnchorData.java
new file mode 100644
index 0000000..23b87dc
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ClientAnchorData.java
@@ -0,0 +1,123 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.util.Internal;
+
+/**
+ * A client anchor is attached to an excel worksheet. It anchors against
+ * absolute coordinates, a top-left cell and fixed height and width, or
+ * a top-left and bottom-right cell, depending on the {@link ClientAnchorData.AnchorType}:
+ *
+ * {@link ClientAnchor.AnchorType#DONT_MOVE_AND_RESIZE} == absolute top-left coordinates and width/height, no
+ * cell references
+ * {@link ClientAnchor.AnchorType#MOVE_DONT_RESIZE} == fixed top-left cell reference, absolute width/height
+ * {@link ClientAnchor.AnchorType#MOVE_AND_RESIZE} == fixed top-left and bottom-right cell references, dynamic
+ * width/height
+ *
+ * Note this class only reports the current values for possibly calculated positions and sizes.
+ * If the sheet row/column sizes or positions shift, this needs updating via external calculations.
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ClientAnchorData extends CoordinateData {
+ /**
+ * top
+ */
+ private Integer top;
+
+ /**
+ * right
+ */
+ private Integer right;
+
+ /**
+ * bottom
+ */
+ private Integer bottom;
+
+ /**
+ * left
+ */
+ private Integer left;
+
+ /**
+ * anchor type
+ */
+ private AnchorType anchorType;
+
+ @Getter
+ public enum AnchorType {
+ /**
+ * Move and Resize With Anchor Cells (0)
+ *
+ * Specifies that the current drawing shall move and
+ * resize to maintain its row and column anchors (i.e. the
+ * object is anchored to the actual from and to row and column)
+ *
+ */
+ MOVE_AND_RESIZE(ClientAnchor.AnchorType.MOVE_AND_RESIZE),
+
+ /**
+ * Don't Move but do Resize With Anchor Cells (1)
+ *
+ * Specifies that the current drawing shall not move with its
+ * row and column, but should be resized. This option is not normally
+ * used, but is included for completeness.
+ *
+ * Note: Excel has no setting for this combination, nor does the ECMA standard.
+ */
+ DONT_MOVE_DO_RESIZE(ClientAnchor.AnchorType.DONT_MOVE_DO_RESIZE),
+
+ /**
+ * Move With Cells but Do Not Resize (2)
+ *
+ * Specifies that the current drawing shall move with its
+ * row and column (i.e. the object is anchored to the
+ * actual from row and column), but that the size shall remain absolute.
+ *
+ *
+ * If additional rows/columns are added between the from and to locations of the drawing,
+ * the drawing shall move its to anchors as needed to maintain this same absolute size.
+ *
+ */
+ MOVE_DONT_RESIZE(ClientAnchor.AnchorType.MOVE_DONT_RESIZE),
+
+ /**
+ * Do Not Move or Resize With Underlying Rows/Columns (3)
+ *
+ * Specifies that the current start and end positions shall
+ * be maintained with respect to the distances from the
+ * absolute start point of the worksheet.
+ *
+ *
+ * If additional rows/columns are added before the
+ * drawing, the drawing shall move its anchors as needed
+ * to maintain this same absolute position.
+ *
+ */
+ DONT_MOVE_AND_RESIZE(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
+
+ ClientAnchor.AnchorType value;
+
+ AnchorType(ClientAnchor.AnchorType value) {
+ this.value = value;
+ }
+
+ /**
+ * return the AnchorType corresponding to the code
+ *
+ * @param value the anchor type code
+ * @return the anchor type enum
+ */
+ @Internal
+ public static ClientAnchorData.AnchorType byId(int value) {
+ return values()[value];
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CommentData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CommentData.java
new file mode 100644
index 0000000..60d69f5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CommentData.java
@@ -0,0 +1,24 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * comment
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CommentData extends ClientAnchorData {
+ /**
+ * Name of the original comment author
+ */
+ private String author;
+ /**
+ * rich text string
+ */
+ private RichTextStringData richTextStringData;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CoordinateData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CoordinateData.java
new file mode 100644
index 0000000..ece4e25
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/CoordinateData.java
@@ -0,0 +1,49 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * coordinate.
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class CoordinateData {
+ /**
+ * first row index.Priority is higher than {@link #relativeFirstRowIndex}.
+ */
+ private Integer firstRowIndex;
+ /**
+ * first column index.Priority is higher than {@link #relativeFirstColumnIndex}.
+ */
+ private Integer firstColumnIndex;
+ /**
+ * last row index.Priority is higher than {@link #relativeLastRowIndex}.
+ */
+ private Integer lastRowIndex;
+ /**
+ * last column index.Priority is higher than {@link #relativeLastColumnIndex}.
+ */
+ private Integer lastColumnIndex;
+
+ /**
+ * relative first row index
+ */
+ private Integer relativeFirstRowIndex;
+ /**
+ * relative first column index
+ */
+ private Integer relativeFirstColumnIndex;
+ /**
+ * relative last row index
+ */
+ private Integer relativeLastRowIndex;
+ /**
+ *relative last column index
+ */
+ private Integer relativeLastColumnIndex;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/DataFormatData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/DataFormatData.java
new file mode 100644
index 0000000..5b285f0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/DataFormatData.java
@@ -0,0 +1,53 @@
+package ai.chat2db.excel.metadata.data;
+
+import ai.chat2db.excel.util.StringUtils;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * data format
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class DataFormatData {
+ /**
+ * index
+ */
+ private Short index;
+
+ /**
+ * format
+ */
+ private String format;
+
+ /**
+ * The source is not empty merge the data to the target.
+ *
+ * @param source source
+ * @param target target
+ */
+ public static void merge(DataFormatData source, DataFormatData target) {
+ if (source == null || target == null) {
+ return;
+ }
+ if (source.getIndex() != null) {
+ target.setIndex(source.getIndex());
+ }
+ if (StringUtils.isNotBlank(source.getFormat())) {
+ target.setFormat(source.getFormat());
+ }
+ }
+
+ @Override
+ public DataFormatData clone() {
+ DataFormatData dataFormatData = new DataFormatData();
+ dataFormatData.setIndex(getIndex());
+ dataFormatData.setFormat(getFormat());
+ return dataFormatData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/FormulaData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/FormulaData.java
new file mode 100644
index 0000000..a8e186c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/FormulaData.java
@@ -0,0 +1,27 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * formula
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class FormulaData {
+ /**
+ * formula
+ */
+ private String formulaValue;
+
+ @Override
+ public FormulaData clone() {
+ FormulaData formulaData = new FormulaData();
+ formulaData.setFormulaValue(getFormulaValue());
+ return formulaData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/HyperlinkData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/HyperlinkData.java
new file mode 100644
index 0000000..dd96ba7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/HyperlinkData.java
@@ -0,0 +1,58 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * hyperlink
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class HyperlinkData extends CoordinateData {
+ /**
+ * Depending on the hyperlink type it can be URL, e-mail, path to a file, etc
+ */
+ private String address;
+ /**
+ * hyperlink type
+ */
+ private HyperlinkType hyperlinkType;
+
+ @Getter
+ public enum HyperlinkType {
+ /**
+ * Not a hyperlink
+ */
+ NONE(org.apache.poi.common.usermodel.HyperlinkType.NONE),
+
+ /**
+ * Link to an existing file or web page
+ */
+ URL(org.apache.poi.common.usermodel.HyperlinkType.URL),
+
+ /**
+ * Link to a place in this document
+ */
+ DOCUMENT(org.apache.poi.common.usermodel.HyperlinkType.DOCUMENT),
+
+ /**
+ * Link to an E-mail address
+ */
+ EMAIL(org.apache.poi.common.usermodel.HyperlinkType.EMAIL),
+
+ /**
+ * Link to a file
+ */
+ FILE(org.apache.poi.common.usermodel.HyperlinkType.FILE);
+
+ org.apache.poi.common.usermodel.HyperlinkType value;
+
+ HyperlinkType(org.apache.poi.common.usermodel.HyperlinkType value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ImageData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ImageData.java
new file mode 100644
index 0000000..5e92c5a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ImageData.java
@@ -0,0 +1,63 @@
+package ai.chat2db.excel.metadata.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * image
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ImageData extends ClientAnchorData {
+
+ /**
+ * image
+ */
+ private byte[] image;
+
+ /**
+ * image type
+ */
+ private ImageType imageType;
+
+ @Getter
+ public enum ImageType {
+ /**
+ * Extended windows meta file
+ */
+ PICTURE_TYPE_EMF(2),
+ /**
+ * Windows Meta File
+ */
+ PICTURE_TYPE_WMF(3),
+ /**
+ * Mac PICT format
+ */
+ PICTURE_TYPE_PICT(4),
+ /**
+ * JPEG format
+ */
+ PICTURE_TYPE_JPEG(5),
+ /**
+ * PNG format
+ */
+ PICTURE_TYPE_PNG(6),
+ /**
+ * Device independent bitmap
+ */
+ PICTURE_TYPE_DIB(7),
+
+ ;
+
+ int value;
+
+ ImageType(int value) {
+ this.value = value;
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ReadCellData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ReadCellData.java
new file mode 100644
index 0000000..fda714f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/ReadCellData.java
@@ -0,0 +1,161 @@
+package ai.chat2db.excel.metadata.data;
+
+import java.math.BigDecimal;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.constant.EasyExcelConstants;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * read cell data
+ *
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public class ReadCellData extends CellData {
+
+ /**
+ * originalNumberValue vs numberValue
+ *
+ *
+ * NUMBER:
+ * originalNumberValue: Original data and the accuracy of his is 17, but in fact the excel only 15 precision to
+ * process the data
+ * numberValue: After correction of the data and the accuracy of his is 15
+ * for example, originalNumberValue = `2087.0249999999996` , numberValue = `2087.03`
+ *
+ *
+ * DATE:
+ * originalNumberValue: Storage is a data type double, accurate to milliseconds
+ * dateValue: Based on double converted to a date format, he will revised date difference, accurate to seconds
+ * for example, originalNumberValue = `44729.99998836806` ,time is:`2022-06-17 23:59:58.995`,
+ * But in excel is displayed:` 2022-06-17 23:59:59`, dateValue = `2022-06-17 23:59:59`
+ *
+ *
+ * {@link CellDataTypeEnum#NUMBER} {@link CellDataTypeEnum#DATE}
+ */
+ private BigDecimal originalNumberValue;
+
+ /**
+ * data format.
+ */
+ private DataFormatData dataFormatData;
+
+ public ReadCellData(CellDataTypeEnum type) {
+ super();
+ if (type == null) {
+ throw new IllegalArgumentException("Type can not be null");
+ }
+ setType(type);
+ }
+
+ public ReadCellData(T data) {
+ super();
+ setData(data);
+ }
+
+ public ReadCellData(String stringValue) {
+ this(CellDataTypeEnum.STRING, stringValue);
+ }
+
+ public ReadCellData(CellDataTypeEnum type, String stringValue) {
+ super();
+ if (type != CellDataTypeEnum.STRING && type != CellDataTypeEnum.ERROR) {
+ throw new IllegalArgumentException("Only support CellDataTypeEnum.STRING and CellDataTypeEnum.ERROR");
+ }
+ if (stringValue == null) {
+ throw new IllegalArgumentException("StringValue can not be null");
+ }
+ setType(type);
+ setStringValue(stringValue);
+ }
+
+ public ReadCellData(BigDecimal numberValue) {
+ super();
+ if (numberValue == null) {
+ throw new IllegalArgumentException("DoubleValue can not be null");
+ }
+ setType(CellDataTypeEnum.NUMBER);
+ setNumberValue(numberValue);
+ }
+
+ public ReadCellData(Boolean booleanValue) {
+ super();
+ if (booleanValue == null) {
+ throw new IllegalArgumentException("BooleanValue can not be null");
+ }
+ setType(CellDataTypeEnum.BOOLEAN);
+ setBooleanValue(booleanValue);
+ }
+
+ public static ReadCellData> newEmptyInstance() {
+ return newEmptyInstance(null, null);
+ }
+
+ public static ReadCellData> newEmptyInstance(Integer rowIndex, Integer columnIndex) {
+ ReadCellData> cellData = new ReadCellData<>(CellDataTypeEnum.EMPTY);
+ cellData.setRowIndex(rowIndex);
+ cellData.setColumnIndex(columnIndex);
+ return cellData;
+ }
+
+ public static ReadCellData> newInstance(Boolean booleanValue) {
+ return newInstance(booleanValue, null, null);
+ }
+
+ public static ReadCellData> newInstance(Boolean booleanValue, Integer rowIndex, Integer columnIndex) {
+ ReadCellData> cellData = new ReadCellData<>(booleanValue);
+ cellData.setRowIndex(rowIndex);
+ cellData.setColumnIndex(columnIndex);
+ return cellData;
+ }
+
+ public static ReadCellData> newInstance(String stringValue, Integer rowIndex, Integer columnIndex) {
+ ReadCellData> cellData = new ReadCellData<>(stringValue);
+ cellData.setRowIndex(rowIndex);
+ cellData.setColumnIndex(columnIndex);
+ return cellData;
+ }
+
+ public static ReadCellData> newInstance(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) {
+ ReadCellData> cellData = new ReadCellData<>(numberValue);
+ cellData.setRowIndex(rowIndex);
+ cellData.setColumnIndex(columnIndex);
+ return cellData;
+ }
+
+ public static ReadCellData> newInstanceOriginal(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) {
+ ReadCellData> cellData = new ReadCellData<>(numberValue);
+ cellData.setRowIndex(rowIndex);
+ cellData.setColumnIndex(columnIndex);
+ cellData.setOriginalNumberValue(numberValue);
+ cellData.setNumberValue(numberValue.round(EasyExcelConstants.EXCEL_MATH_CONTEXT));
+ return cellData;
+ }
+
+ @Override
+ public ReadCellData clone() {
+ ReadCellData readCellData = new ReadCellData<>();
+ readCellData.setType(getType());
+ readCellData.setNumberValue(getNumberValue());
+ readCellData.setOriginalNumberValue(getOriginalNumberValue());
+ readCellData.setStringValue(getStringValue());
+ readCellData.setBooleanValue(getBooleanValue());
+ readCellData.setData(getData());
+ if (getDataFormatData() != null) {
+ readCellData.setDataFormatData(getDataFormatData().clone());
+ }
+ if (getFormulaData() != null) {
+ readCellData.setFormulaData(getFormulaData().clone());
+ }
+ return readCellData;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/RichTextStringData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/RichTextStringData.java
new file mode 100644
index 0000000..dde43ba
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/RichTextStringData.java
@@ -0,0 +1,65 @@
+package ai.chat2db.excel.metadata.data;
+
+import java.util.List;
+
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.write.metadata.style.WriteFont;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * rich text string
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public class RichTextStringData {
+ private String textString;
+ private WriteFont writeFont;
+ private List intervalFontList;
+
+ public RichTextStringData(String textString) {
+ this.textString = textString;
+ }
+
+ @Getter
+ @Setter
+ @EqualsAndHashCode
+ @AllArgsConstructor
+ public static class IntervalFont {
+ private Integer startIndex;
+ private Integer endIndex;
+ private WriteFont writeFont;
+ }
+
+ /**
+ * Applies a font to the specified characters of a string.
+ *
+ * @param startIndex The start index to apply the font to (inclusive)
+ * @param endIndex The end index to apply to font to (exclusive)
+ * @param writeFont The font to use.
+ */
+ public void applyFont(int startIndex, int endIndex, WriteFont writeFont) {
+ if (intervalFontList == null) {
+ intervalFontList = ListUtils.newArrayList();
+ }
+ intervalFontList.add(new IntervalFont(startIndex, endIndex, writeFont));
+ }
+
+ /**
+ * Sets the font of the entire string.
+ *
+ * @param writeFont The font to use.
+ */
+ public void applyFont(WriteFont writeFont) {
+ this.writeFont = writeFont;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/WriteCellData.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/WriteCellData.java
new file mode 100644
index 0000000..ea719f8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/data/WriteCellData.java
@@ -0,0 +1,143 @@
+package ai.chat2db.excel.metadata.data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.List;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.write.metadata.style.WriteCellStyle;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.poi.ss.usermodel.CellStyle;
+
+/**
+ * write cell data
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public class WriteCellData extends CellData {
+ /**
+ * Support only when writing.{@link CellDataTypeEnum#DATE}
+ */
+ private LocalDateTime dateValue;
+
+ /**
+ * rich text.{@link CellDataTypeEnum#RICH_TEXT_STRING}
+ */
+ private RichTextStringData richTextStringDataValue;
+ /**
+ * image
+ */
+ private List imageDataList;
+ /**
+ * comment
+ */
+ private CommentData commentData;
+ /**
+ * hyper link
+ */
+ private HyperlinkData hyperlinkData;
+
+ /**
+ * style
+ */
+ private WriteCellStyle writeCellStyle;
+
+ /**
+ * If originCellStyle is empty, one will be created.
+ * If both writeCellStyle and originCellStyle exist, copy from writeCellStyle to originCellStyle.
+ */
+ private CellStyle originCellStyle;
+
+
+ public WriteCellData(String stringValue) {
+ this(CellDataTypeEnum.STRING, stringValue);
+ }
+
+ public WriteCellData(CellDataTypeEnum type) {
+ super();
+ setType(type);
+ }
+
+ public WriteCellData(CellDataTypeEnum type, String stringValue) {
+ super();
+ if (type != CellDataTypeEnum.STRING && type != CellDataTypeEnum.ERROR) {
+ throw new IllegalArgumentException("Only support CellDataTypeEnum.STRING and CellDataTypeEnum.ERROR");
+ }
+ if (stringValue == null) {
+ throw new IllegalArgumentException("StringValue can not be null");
+ }
+ setType(type);
+ setStringValue(stringValue);
+ }
+
+ public WriteCellData(BigDecimal numberValue) {
+ super();
+ if (numberValue == null) {
+ throw new IllegalArgumentException("DoubleValue can not be null");
+ }
+ setType(CellDataTypeEnum.NUMBER);
+ setNumberValue(numberValue);
+ }
+
+ public WriteCellData(Boolean booleanValue) {
+ super();
+ if (booleanValue == null) {
+ throw new IllegalArgumentException("BooleanValue can not be null");
+ }
+ setType(CellDataTypeEnum.BOOLEAN);
+ setBooleanValue(booleanValue);
+ }
+
+ public WriteCellData(Date dateValue) {
+ super();
+ if (dateValue == null) {
+ throw new IllegalArgumentException("DateValue can not be null");
+ }
+ setType(CellDataTypeEnum.DATE);
+ this.dateValue = LocalDateTime.ofInstant(dateValue.toInstant(), ZoneId.systemDefault());
+ }
+
+ public WriteCellData(LocalDateTime dateValue) {
+ super();
+ if (dateValue == null) {
+ throw new IllegalArgumentException("DateValue can not be null");
+ }
+ setType(CellDataTypeEnum.DATE);
+ this.dateValue = dateValue;
+ }
+
+ public WriteCellData(byte[] image) {
+ super();
+ if (image == null) {
+ throw new IllegalArgumentException("Image can not be null");
+ }
+ setType(CellDataTypeEnum.EMPTY);
+ this.imageDataList = ListUtils.newArrayList();
+ ImageData imageData = new ImageData();
+ imageData.setImage(image);
+ imageDataList.add(imageData);
+ }
+
+ /**
+ * Return a style, if is empty, create a new
+ *
+ * @return not null.
+ */
+ public WriteCellStyle getOrCreateStyle() {
+ if (this.writeCellStyle == null) {
+ this.writeCellStyle = new WriteCellStyle();
+ }
+ return this.writeCellStyle;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/DataFormatter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/DataFormatter.java
new file mode 100644
index 0000000..0feef88
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/DataFormatter.java
@@ -0,0 +1,873 @@
+/*
+ * ==================================================================== Licensed to the Apache Software Foundation (ASF)
+ * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ * 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff
+ * can be found in svn at location root/projects/3rd-party/src
+ * ====================================================================
+ */
+package ai.chat2db.excel.metadata.format;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DateFormatSymbols;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ai.chat2db.excel.util.DateUtils;
+
+import org.apache.poi.ss.format.CellFormat;
+import org.apache.poi.ss.format.CellFormatResult;
+import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter;
+import org.apache.poi.ss.usermodel.FractionFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date
+ * conversion.
+ *
+ * This is a non-thread-safe class.
+ *
+ * @author Jiaju Zhuang
+ */
+public class DataFormatter {
+ /**
+ * For logging any problems we find
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(DataFormatter.class);
+ private static final String defaultFractionWholePartFormat = "#";
+ private static final String defaultFractionFractionPartFormat = "#/##";
+ /**
+ * Pattern to find a number format: "0" or "#"
+ */
+ private static final Pattern numPattern = Pattern.compile("[0#]+");
+
+ /**
+ * Pattern to find days of week as text "ddd...."
+ */
+ private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Pattern to find "AM/PM" marker
+ */
+ private static final Pattern amPmPattern =
+ Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Pattern to find formats with condition ranges e.g. [>=100]
+ */
+ private static final Pattern rangeConditionalPattern =
+ Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*");
+
+ /**
+ * A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into
+ * locales
+ */
+ private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])");
+
+ /**
+ * A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red,
+ * White, Yellow, "Color n" (1<=n<=56)
+ */
+ private static final Pattern colorPattern = Pattern.compile(
+ "(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" + "(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|"
+ + "(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])|(\\[DBNum(1|2|3)])|(\\[\\$-\\d{0,3}])",
+ Pattern.CASE_INSENSITIVE);
+
+ /**
+ * A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called
+ */
+ private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)");
+
+ /**
+ * A regex to strip junk out of fraction formats
+ */
+ private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ ?#\\d/]+)");
+
+ /**
+ * A regex to detect if an alternate grouping character is used in a numeric format
+ */
+ private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})");
+
+ private static final Pattern E_NOTATION_PATTERN = Pattern.compile("E(\\d)");
+
+ /**
+ * Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs
+ * ("#").
+ */
+ private static final String invalidDateTimeString;
+
+ static {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < 255; i++) {buf.append('#');}
+ invalidDateTimeString = buf.toString();
+ }
+
+ /**
+ * The decimal symbols of the locale used for formatting values.
+ */
+ private DecimalFormatSymbols decimalSymbols;
+
+ /**
+ * The date symbols of the locale used for formatting values.
+ */
+ private DateFormatSymbols dateSymbols;
+ /**
+ * A default format to use when a number pattern cannot be parsed.
+ */
+ private Format defaultNumFormat;
+ /**
+ * A map to cache formats. Map formats
+ */
+ private final Map formats = new HashMap();
+
+ /**
+ * stores the locale valid it the last formatting call
+ */
+ private Locale locale;
+ /**
+ * true if date uses 1904 windowing, or false if using 1900 date windowing.
+ *
+ * default is false
+ *
+ * @return
+ */
+ private Boolean use1904windowing;
+ /**
+ * Whether to use scientific Format.
+ *
+ * default is false
+ */
+ private Boolean useScientificFormat;
+
+ /**
+ * Creates a formatter using the given locale.
+ */
+ public DataFormatter(Boolean use1904windowing, Locale locale, Boolean useScientificFormat) {
+ if (use1904windowing == null) {
+ this.use1904windowing = Boolean.FALSE;
+ } else {
+ this.use1904windowing = use1904windowing;
+ }
+
+ if (locale == null) {
+ this.locale = Locale.getDefault();
+ } else {
+ this.locale = locale;
+ }
+
+ if (use1904windowing == null) {
+ this.useScientificFormat = Boolean.FALSE;
+ } else {
+ this.useScientificFormat = useScientificFormat;
+ }
+
+ this.dateSymbols = DateFormatSymbols.getInstance(this.locale);
+ this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale);
+ }
+
+ private Format getFormat(Double data, Short dataFormat, String dataFormatString) {
+
+ // Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
+ // That however would require other code to be re factored.
+ // String[] formatBits = formatStrIn.split(";");
+ // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
+ // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
+ String formatStr = dataFormatString;
+
+ // Excel supports 2+ part conditional data formats, eg positive/negative/zero,
+ // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds
+ // of different formats for different ranges, just +ve/-ve, we need to
+ // handle these ourselves in a special way.
+ // For now, if we detect 2+ parts, we call out to CellFormat to handle it
+ // TODO Going forward, we should really merge the logic between the two classes
+ if (formatStr.contains(";") &&
+ (formatStr.indexOf(';') != formatStr.lastIndexOf(';')
+ || rangeConditionalPattern.matcher(formatStr).matches()
+ )) {
+ try {
+ // Ask CellFormat to get a formatter for it
+ CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
+ // CellFormat requires callers to identify date vs not, so do so
+ Object cellValueO = data;
+ if (DateUtils.isADateFormat(dataFormat, formatStr) &&
+ // don't try to handle Date value 0, let a 3 or 4-part format take care of it
+ data.doubleValue() != 0.0) {
+ cellValueO = DateUtils.getJavaDate(data, use1904windowing);
+ }
+ // Wrap and return (non-cachable - CellFormat does that)
+ return new CellFormatResultWrapper(cfmt.apply(cellValueO));
+ } catch (Exception e) {
+ LOGGER.warn("Formatting failed for format {}, falling back", formatStr, e);
+ }
+ }
+
+ // See if we already have it cached
+ Format format = formats.get(formatStr);
+ if (format != null) {
+ return format;
+ }
+
+ // Is it one of the special built in types, General or @?
+ if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
+ format = getDefaultFormat();
+ addFormat(formatStr, format);
+ return format;
+ }
+
+ // Build a formatter, and cache it
+ format = createFormat(dataFormat, formatStr);
+ addFormat(formatStr, format);
+ return format;
+ }
+
+
+
+ private Format createFormat(Short dataFormat, String dataFormatString) {
+ String formatStr = dataFormatString;
+
+ Format format = checkSpecialConverter(formatStr);
+ if (format != null) {
+ return format;
+ }
+
+ // Remove colour formatting if present
+ Matcher colourM = colorPattern.matcher(formatStr);
+ while (colourM.find()) {
+ String colour = colourM.group();
+
+ // Paranoid replacement...
+ int at = formatStr.indexOf(colour);
+ if (at == -1) {break;}
+ String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length());
+ if (nFormatStr.equals(formatStr)) {break;}
+
+ // Try again in case there's multiple
+ formatStr = nFormatStr;
+ colourM = colorPattern.matcher(formatStr);
+ }
+
+ // Strip off the locale information, we use an instance-wide locale for everything
+ Matcher m = localePatternGroup.matcher(formatStr);
+ while (m.find()) {
+ String match = m.group();
+ String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
+ if (symbol.indexOf('$') > -1) {
+ symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + symbol.substring(symbol.indexOf('$'));
+ }
+ formatStr = m.replaceAll(symbol);
+ m = localePatternGroup.matcher(formatStr);
+ }
+
+ // Check for special cases
+ if (formatStr == null || formatStr.trim().length() == 0) {
+ return getDefaultFormat();
+ }
+
+ if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
+ return getDefaultFormat();
+ }
+
+ if (DateUtils.isADateFormat(dataFormat, formatStr)) {
+ return createDateFormat(formatStr);
+ }
+ // Excel supports fractions in format strings, which Java doesn't
+ if (formatStr.contains("#/") || formatStr.contains("?/")) {
+ String[] chunks = formatStr.split(";");
+ for (String chunk1 : chunks) {
+ String chunk = chunk1.replaceAll("\\?", "#");
+ Matcher matcher = fractionStripper.matcher(chunk);
+ chunk = matcher.replaceAll(" ");
+ chunk = chunk.replaceAll(" +", " ");
+ Matcher fractionMatcher = fractionPattern.matcher(chunk);
+ // take the first match
+ if (fractionMatcher.find()) {
+ String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat;
+ return new FractionFormat(wholePart, fractionMatcher.group(3));
+ }
+ }
+
+ // Strip custom text in quotes and escaped characters for now as it can cause performance problems in
+ // fractions.
+ // String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.",
+ // "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#");
+ return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat);
+ }
+
+ if (numPattern.matcher(formatStr).find()) {
+ return createNumberFormat(formatStr);
+ }
+ return getDefaultFormat();
+ }
+
+ private Format checkSpecialConverter(String dataFormatString) {
+ if ("00000\\-0000".equals(dataFormatString) || "00000-0000".equals(dataFormatString)) {
+ return new ZipPlusFourFormat();
+ }
+ if ("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString)
+ || "[<=9999999]###-####;(###) ###-####".equals(dataFormatString)
+ || "###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString)
+ || "###-####;(###) ###-####".equals(dataFormatString)) {
+ return new PhoneFormat();
+ }
+ if ("000\\-00\\-0000".equals(dataFormatString) || "000-00-0000".equals(dataFormatString)) {
+ return new SSNFormat();
+ }
+ return null;
+ }
+
+ private Format createDateFormat(String pFormatStr) {
+ String formatStr = pFormatStr;
+ formatStr = formatStr.replaceAll("\\\\-", "-");
+ formatStr = formatStr.replaceAll("\\\\,", ",");
+ formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char
+ formatStr = formatStr.replaceAll("\\\\ ", " ");
+ formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy
+ formatStr = formatStr.replaceAll(";@", "");
+ formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy
+ formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting
+ formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates
+ formatStr = formatStr.replace("\"", "");
+
+ boolean hasAmPm = false;
+ Matcher amPmMatcher = amPmPattern.matcher(formatStr);
+ while (amPmMatcher.find()) {
+ formatStr = amPmMatcher.replaceAll("@");
+ hasAmPm = true;
+ amPmMatcher = amPmPattern.matcher(formatStr);
+ }
+ formatStr = formatStr.replaceAll("@", "a");
+
+ Matcher dateMatcher = daysAsText.matcher(formatStr);
+ if (dateMatcher.find()) {
+ String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E");
+ formatStr = dateMatcher.replaceAll(match);
+ }
+
+ // Convert excel date format to SimpleDateFormat.
+ // Excel uses lower and upper case 'm' for both minutes and months.
+ // From Excel help:
+ /*
+ The "m" or "mm" code must appear immediately after the "h" or"hh"
+ code or immediately before the "ss" code; otherwise, Microsoft
+ Excel displays the month instead of minutes."
+ */
+ StringBuilder sb = new StringBuilder();
+ char[] chars = formatStr.toCharArray();
+ boolean mIsMonth = true;
+ List ms = new ArrayList();
+ boolean isElapsed = false;
+ for (int j = 0; j < chars.length; j++) {
+ char c = chars[j];
+ if (c == '\'') {
+ sb.append(c);
+ j++;
+
+ // skip until the next quote
+ while (j < chars.length) {
+ c = chars[j];
+ sb.append(c);
+ if (c == '\'') {
+ break;
+ }
+ j++;
+ }
+ } else if (c == '[' && !isElapsed) {
+ isElapsed = true;
+ mIsMonth = false;
+ sb.append(c);
+ } else if (c == ']' && isElapsed) {
+ isElapsed = false;
+ sb.append(c);
+ } else if (isElapsed) {
+ if (c == 'h' || c == 'H') {
+ sb.append('H');
+ } else if (c == 'm' || c == 'M') {
+ sb.append('m');
+ } else if (c == 's' || c == 'S') {
+ sb.append('s');
+ } else {
+ sb.append(c);
+ }
+ } else if (c == 'h' || c == 'H') {
+ mIsMonth = false;
+ if (hasAmPm) {
+ sb.append('h');
+ } else {
+ sb.append('H');
+ }
+ } else if (c == 'm' || c == 'M') {
+ if (mIsMonth) {
+ sb.append('M');
+ ms.add(Integer.valueOf(sb.length() - 1));
+ } else {
+ sb.append('m');
+ }
+ } else if (c == 's' || c == 'S') {
+ sb.append('s');
+ // if 'M' precedes 's' it should be minutes ('m')
+ for (int index : ms) {
+ if (sb.charAt(index) == 'M') {
+ sb.replace(index, index + 1, "m");
+ }
+ }
+ mIsMonth = true;
+ ms.clear();
+ } else if (Character.isLetter(c)) {
+ mIsMonth = true;
+ ms.clear();
+ if (c == 'y' || c == 'Y') {
+ sb.append('y');
+ } else if (c == 'd' || c == 'D') {
+ sb.append('d');
+ } else {
+ sb.append(c);
+ }
+ } else {
+ if (Character.isWhitespace(c)) {
+ ms.clear();
+ }
+ sb.append(c);
+ }
+ }
+ formatStr = sb.toString();
+
+ try {
+ return new ExcelStyleDateFormatter(formatStr, dateSymbols);
+ } catch (IllegalArgumentException iae) {
+ LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae);
+ // the pattern could not be parsed correctly,
+ // so fall back to the default number format
+ return getDefaultFormat();
+ }
+
+ }
+
+ private String cleanFormatForNumber(String formatStr) {
+ StringBuilder sb = new StringBuilder(formatStr);
+ // If they requested spacers, with "_",
+ // remove those as we don't do spacing
+ // If they requested full-column-width
+ // padding, with "*", remove those too
+ for (int i = 0; i < sb.length(); i++) {
+ char c = sb.charAt(i);
+ if (c == '_' || c == '*') {
+ if (i > 0 && sb.charAt((i - 1)) == '\\') {
+ // It's escaped, don't worry
+ continue;
+ }
+ if (i < sb.length() - 1) {
+ // Remove the character we're supposed
+ // to match the space of / pad to the
+ // column width with
+ sb.deleteCharAt(i + 1);
+ }
+ // Remove the _ too
+ sb.deleteCharAt(i);
+ i--;
+ }
+ }
+
+ // Now, handle the other aspects like
+ // quoting and scientific notation
+ for (int i = 0; i < sb.length(); i++) {
+ char c = sb.charAt(i);
+ // remove quotes and back slashes
+ if (c == '\\' || c == '"') {
+ sb.deleteCharAt(i);
+ i--;
+
+ // for scientific/engineering notation
+ } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
+ sb.deleteCharAt(i);
+ i--;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private static class InternalDecimalFormatWithScale extends Format {
+
+ private static final Pattern endsWithCommas = Pattern.compile("(,+)$");
+ private BigDecimal divider;
+ private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000);
+ private final DecimalFormat df;
+
+ private static String trimTrailingCommas(String s) {
+ return s.replaceAll(",+$", "");
+ }
+
+ public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) {
+ df = new DecimalFormat(trimTrailingCommas(pattern), symbols);
+ setExcelStyleRoundingMode(df);
+ Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern);
+ if (endsWithCommasMatcher.find()) {
+ String commas = (endsWithCommasMatcher.group(1));
+ BigDecimal temp = BigDecimal.ONE;
+ for (int i = 0; i < commas.length(); ++i) {
+ temp = temp.multiply(ONE_THOUSAND);
+ }
+ divider = temp;
+ } else {
+ divider = null;
+ }
+ }
+
+ private Object scaleInput(Object obj) {
+ if (divider != null) {
+ if (obj instanceof BigDecimal) {
+ obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP);
+ } else if (obj instanceof Double) {
+ obj = (Double)obj / divider.doubleValue();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return obj;
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ obj = scaleInput(obj);
+ return df.format(obj, toAppendTo, pos);
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Format createNumberFormat(String formatStr) {
+ String format = cleanFormatForNumber(formatStr);
+ DecimalFormatSymbols symbols = decimalSymbols;
+
+ // Do we need to change the grouping character?
+ // eg for a format like #'##0 which wants 12'345 not 12,345
+ Matcher agm = alternateGrouping.matcher(format);
+ if (agm.find()) {
+ char grouping = agm.group(2).charAt(0);
+ // Only replace the grouping character if it is not the default
+ // grouping character for the US locale (',') in order to enable
+ // correct grouping for non-US locales.
+ if (grouping != ',') {
+ symbols = DecimalFormatSymbols.getInstance(locale);
+
+ symbols.setGroupingSeparator(grouping);
+ String oldPart = agm.group(1);
+ String newPart = oldPart.replace(grouping, ',');
+ format = format.replace(oldPart, newPart);
+ }
+ }
+
+ try {
+ return new InternalDecimalFormatWithScale(format, symbols);
+ } catch (IllegalArgumentException iae) {
+ LOGGER.error("Formatting failed for format {}, falling back", formatStr, iae);
+ // the pattern could not be parsed correctly,
+ // so fall back to the default number format
+ return getDefaultFormat();
+ }
+ }
+
+ private Format getDefaultFormat() {
+ // for numeric cells try user supplied default
+ if (defaultNumFormat != null) {
+ return defaultNumFormat;
+ // otherwise use general format
+ }
+ defaultNumFormat = new ExcelGeneralNumberFormat(locale, useScientificFormat);
+ return defaultNumFormat;
+ }
+
+ /**
+ * Performs Excel-style date formatting, using the supplied Date and format
+ */
+ private String performDateFormatting(Date d, Format dateFormat) {
+ Format df = dateFormat != null ? dateFormat : getDefaultFormat();
+ return df.format(d);
+ }
+
+ /**
+ * Returns the formatted value of an Excel date as a String based on the cell's DataFormat .
+ * i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc.
+ *
+ * If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a
+ * number format, or no rules apply, the cell's style format is used. If the style does not have a format, the
+ * default date format is applied.
+ *
+ * @param data to format
+ * @param dataFormat
+ * @param dataFormatString
+ * @return Formatted value
+ */
+ private String getFormattedDateString(Double data, Short dataFormat, String dataFormatString) {
+ Format dateFormat = getFormat(data, dataFormat, dataFormatString);
+ if (dateFormat instanceof ExcelStyleDateFormatter) {
+ // Hint about the raw excel value
+ ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data);
+ }
+ return performDateFormatting(DateUtils.getJavaDate(data, use1904windowing), dateFormat);
+ }
+
+ /**
+ * Returns the formatted value of an Excel number as a String based on the cell's DataFormat .
+ * Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800)
+ * 555-1234".
+ *
+ * Format comes from either the highest priority conditional format rule with a specified format, or from the cell
+ * style.
+ *
+ * @param data to format
+ * @param dataFormat
+ * @param dataFormatString
+ * @return a formatted number string
+ */
+ private String getFormattedNumberString(BigDecimal data, Short dataFormat, String dataFormatString) {
+ Format numberFormat = getFormat(data.doubleValue(), dataFormat, dataFormatString);
+ return E_NOTATION_PATTERN.matcher(numberFormat.format(data)).replaceFirst("E+$1");
+ }
+
+ /**
+ * Format data.
+ *
+ * @param data
+ * @param dataFormat
+ * @param dataFormatString
+ * @return
+ */
+ public String format(BigDecimal data, Short dataFormat, String dataFormatString) {
+ if (DateUtils.isADateFormat(dataFormat, dataFormatString)) {
+ return getFormattedDateString(data.doubleValue(), dataFormat, dataFormatString);
+ }
+ return getFormattedNumberString(data, dataFormat, dataFormatString);
+ }
+
+ /**
+ *
+ * Sets a default number format to be used when the Excel format cannot be parsed successfully. Note: This is
+ * a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells
+ * with the General format.
+ *
+ *
+ * The value that will be passed to the Format's format method (specified by java.text.Format#format )
+ * will be a double value from a numeric cell. Therefore the code in the format method should expect a
+ * Number value.
+ *
+ *
+ * @param format A Format instance to be used as a default
+ * @see Format#format
+ */
+ public void setDefaultNumberFormat(Format format) {
+ for (Map.Entry entry : formats.entrySet()) {
+ if (entry.getValue() == defaultNumFormat) {
+ entry.setValue(format);
+ }
+ }
+ defaultNumFormat = format;
+ }
+
+ /**
+ * Adds a new format to the available formats.
+ *
+ * The value that will be passed to the Format's format method (specified by java.text.Format#format )
+ * will be a double value from a numeric cell. Therefore the code in the format method should expect a
+ * Number value.
+ *
+ *
+ * @param excelFormatStr The data format string
+ * @param format A Format instance
+ */
+ public void addFormat(String excelFormatStr, Format format) {
+ formats.put(excelFormatStr, format);
+ }
+
+ // Some custom formats
+
+ /**
+ * @return a DecimalFormat with parseIntegerOnly set true
+ */
+ private static DecimalFormat createIntegerOnlyFormat(String fmt) {
+ DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT);
+ DecimalFormat result = new DecimalFormat(fmt, dsf);
+ result.setParseIntegerOnly(true);
+ return result;
+ }
+
+ /**
+ * Enables excel style rounding mode (round half up) on the Decimal Format given.
+ */
+ public static void setExcelStyleRoundingMode(DecimalFormat format) {
+ setExcelStyleRoundingMode(format, RoundingMode.HALF_UP);
+ }
+
+ /**
+ * Enables custom rounding mode on the given Decimal Format.
+ *
+ * @param format DecimalFormat
+ * @param roundingMode RoundingMode
+ */
+ public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
+ format.setRoundingMode(roundingMode);
+ }
+
+ /**
+ * Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting.
+ *
+ * @author James May
+ */
+ @SuppressWarnings("serial")
+ private static final class SSNFormat extends Format {
+ private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
+
+ private SSNFormat() {
+ // enforce singleton
+ }
+
+ /**
+ * Format a number as an SSN
+ */
+ public static String format(Number num) {
+ String result = df.format(num);
+ return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9);
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ return toAppendTo.append(format((Number)obj));
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return df.parseObject(source, pos);
+ }
+ }
+
+ /**
+ * Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4.
+ *
+ * @author James May
+ */
+ @SuppressWarnings("serial")
+ private static final class ZipPlusFourFormat extends Format {
+ private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
+
+ private ZipPlusFourFormat() {
+ // enforce singleton
+ }
+
+ /**
+ * Format a number as Zip + 4
+ */
+ public static String format(Number num) {
+ String result = df.format(num);
+ return result.substring(0, 5) + '-' + result.substring(5, 9);
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ return toAppendTo.append(format((Number)obj));
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return df.parseObject(source, pos);
+ }
+ }
+
+ /**
+ * Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting.
+ *
+ * @author James May
+ */
+ @SuppressWarnings("serial")
+ private static final class PhoneFormat extends Format {
+ private static final DecimalFormat df = createIntegerOnlyFormat("##########");
+
+ private PhoneFormat() {
+ // enforce singleton
+ }
+
+ /**
+ * Format a number as a phone number
+ */
+ public static String format(Number num) {
+ String result = df.format(num);
+ StringBuilder sb = new StringBuilder();
+ String seg1, seg2, seg3;
+ int len = result.length();
+ if (len <= 4) {
+ return result;
+ }
+
+ seg3 = result.substring(len - 4, len);
+ seg2 = result.substring(Math.max(0, len - 7), len - 4);
+ seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7));
+
+ if (seg1.trim().length() > 0) {
+ sb.append('(').append(seg1).append(") ");
+ }
+ if (seg2.trim().length() > 0) {
+ sb.append(seg2).append('-');
+ }
+ sb.append(seg3);
+ return sb.toString();
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ return toAppendTo.append(format((Number)obj));
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return df.parseObject(source, pos);
+ }
+ }
+
+ /**
+ * Workaround until we merge {@link org.apache.poi.ss.usermodel.DataFormatter} with {@link CellFormat}. Constant,
+ * non-cachable wrapper around a
+ * {@link CellFormatResult}
+ */
+ private final class CellFormatResultWrapper extends Format {
+ private final CellFormatResult result;
+
+ private CellFormatResultWrapper(CellFormatResult result) {
+ this.result = result;
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ return toAppendTo.append(result.text.trim());
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null; // Not supported
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/ExcelGeneralNumberFormat.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/ExcelGeneralNumberFormat.java
new file mode 100644
index 0000000..ab695e8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/format/ExcelGeneralNumberFormat.java
@@ -0,0 +1,81 @@
+package ai.chat2db.excel.metadata.format;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.poi.ss.usermodel.DataFormatter;
+
+/**
+ * Written with reference to {@link org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat }.
+ *
+ * Supported Do not use scientific notation.
+ *
+ * @author JiaJu Zhuang
+ **/
+public class ExcelGeneralNumberFormat extends Format {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final MathContext TO_10_SF = new MathContext(10, RoundingMode.HALF_UP);
+
+ private final DecimalFormatSymbols decimalSymbols;
+ private final DecimalFormat integerFormat;
+ private final DecimalFormat decimalFormat;
+ private final DecimalFormat scientificFormat;
+
+ public ExcelGeneralNumberFormat(final Locale locale, final boolean useScientificFormat) {
+ decimalSymbols = DecimalFormatSymbols.getInstance(locale);
+ // Supported Do not use scientific notation.
+ if (useScientificFormat) {
+ scientificFormat = new DecimalFormat("0.#####E0", decimalSymbols);
+ } else {
+ scientificFormat = new DecimalFormat("#", decimalSymbols);
+ }
+ org.apache.poi.ss.usermodel.DataFormatter.setExcelStyleRoundingMode(scientificFormat);
+ integerFormat = new DecimalFormat("#", decimalSymbols);
+ org.apache.poi.ss.usermodel.DataFormatter.setExcelStyleRoundingMode(integerFormat);
+ decimalFormat = new DecimalFormat("#.##########", decimalSymbols);
+ DataFormatter.setExcelStyleRoundingMode(decimalFormat);
+ }
+
+ @Override
+ public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) {
+ final double value;
+ if (number instanceof Number) {
+ value = ((Number) number).doubleValue();
+ if (Double.isInfinite(value) || Double.isNaN(value)) {
+ return integerFormat.format(number, toAppendTo, pos);
+ }
+ } else {
+ // testBug54786 gets here with a date, so retain previous behaviour
+ return integerFormat.format(number, toAppendTo, pos);
+ }
+
+ final double abs = Math.abs(value);
+ if (abs >= 1E11 || (abs <= 1E-10 && abs > 0)) {
+ return scientificFormat.format(number, toAppendTo, pos);
+ } else if (Math.floor(value) == value || abs >= 1E10) {
+ // integer, or integer portion uses all 11 allowed digits
+ return integerFormat.format(number, toAppendTo, pos);
+ }
+ // Non-integers of non-scientific magnitude are formatted as "up to 11
+ // numeric characters, with the decimal point counting as a numeric
+ // character". We know there is a decimal point, so limit to 10 digits.
+ // https://support.microsoft.com/en-us/kb/65903
+ final double rounded = new BigDecimal(value).round(TO_10_SF).doubleValue();
+ return decimalFormat.format(rounded, toAppendTo, pos);
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ColumnWidthProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ColumnWidthProperty.java
new file mode 100644
index 0000000..710b52c
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ColumnWidthProperty.java
@@ -0,0 +1,31 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.ColumnWidth;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+public class ColumnWidthProperty {
+ private Integer width;
+
+ public ColumnWidthProperty(Integer width) {
+ this.width = width;
+ }
+
+ public static ColumnWidthProperty build(ColumnWidth columnWidth) {
+ if (columnWidth == null || columnWidth.value() < 0) {
+ return null;
+ }
+ return new ColumnWidthProperty(columnWidth.value());
+ }
+
+ public Integer getWidth() {
+ return width;
+ }
+
+ public void setWidth(Integer width) {
+ this.width = width;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/DateTimeFormatProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/DateTimeFormatProperty.java
new file mode 100644
index 0000000..ef244d0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/DateTimeFormatProperty.java
@@ -0,0 +1,34 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.format.DateTimeFormat;
+import ai.chat2db.excel.util.BooleanUtils;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class DateTimeFormatProperty {
+ private String format;
+ private Boolean use1904windowing;
+
+ public DateTimeFormatProperty(String format, Boolean use1904windowing) {
+ this.format = format;
+ this.use1904windowing = use1904windowing;
+ }
+
+ public static DateTimeFormatProperty build(DateTimeFormat dateTimeFormat) {
+ if (dateTimeFormat == null) {
+ return null;
+ }
+ return new DateTimeFormatProperty(dateTimeFormat.value(),
+ BooleanUtils.isTrue(dateTimeFormat.use1904windowing().getBooleanValue()));
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelContentProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelContentProperty.java
new file mode 100644
index 0000000..5ade043
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelContentProperty.java
@@ -0,0 +1,44 @@
+package ai.chat2db.excel.metadata.property;
+
+import java.lang.reflect.Field;
+
+import ai.chat2db.excel.converters.Converter;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author jipengfei
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ExcelContentProperty {
+ public static final ExcelContentProperty EMPTY = new ExcelContentProperty();
+
+ /**
+ * Java field
+ */
+ private Field field;
+ /**
+ * Custom defined converters
+ */
+ private Converter> converter;
+ /**
+ * date time format
+ */
+ private DateTimeFormatProperty dateTimeFormatProperty;
+ /**
+ * number format
+ */
+ private NumberFormatProperty numberFormatProperty;
+ /**
+ * Content style
+ */
+ private StyleProperty contentStyleProperty;
+ /**
+ * Content font
+ */
+ private FontProperty contentFontProperty;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelHeadProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelHeadProperty.java
new file mode 100644
index 0000000..63f3535
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/ExcelHeadProperty.java
@@ -0,0 +1,141 @@
+package ai.chat2db.excel.metadata.property;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import ai.chat2db.excel.enums.HeadKindEnum;
+import ai.chat2db.excel.metadata.ConfigurationHolder;
+import ai.chat2db.excel.metadata.FieldCache;
+import ai.chat2db.excel.metadata.FieldWrapper;
+import ai.chat2db.excel.metadata.Head;
+import ai.chat2db.excel.util.ClassUtils;
+import ai.chat2db.excel.util.StringUtils;
+import ai.chat2db.excel.write.metadata.holder.AbstractWriteHolder;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Define the header attribute of excel
+ *
+ * @author jipengfei
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ExcelHeadProperty {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ExcelHeadProperty.class);
+ /**
+ * Custom class
+ */
+ private Class> headClazz;
+ /**
+ * The types of head
+ */
+ private HeadKindEnum headKind;
+ /**
+ * The number of rows in the line with the most rows
+ */
+ private int headRowNumber;
+ /**
+ * Configuration header information
+ */
+ private Map headMap;
+
+ public ExcelHeadProperty(ConfigurationHolder configurationHolder, Class> headClazz, List> head) {
+ this.headClazz = headClazz;
+ headMap = new TreeMap<>();
+ headKind = HeadKindEnum.NONE;
+ headRowNumber = 0;
+ if (head != null && !head.isEmpty()) {
+ int headIndex = 0;
+ for (int i = 0; i < head.size(); i++) {
+ if (configurationHolder instanceof AbstractWriteHolder) {
+ if (((AbstractWriteHolder)configurationHolder).ignore(null, i)) {
+ continue;
+ }
+ }
+ headMap.put(headIndex, new Head(headIndex, null, null, head.get(i), Boolean.FALSE, Boolean.TRUE));
+ headIndex++;
+ }
+ headKind = HeadKindEnum.STRING;
+ }
+ // convert headClazz to head
+ initColumnProperties(configurationHolder);
+
+ initHeadRowNumber();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind);
+ }
+ }
+
+ private void initHeadRowNumber() {
+ headRowNumber = 0;
+ for (Head head : headMap.values()) {
+ List list = head.getHeadNameList();
+ if (list != null && list.size() > headRowNumber) {
+ headRowNumber = list.size();
+ }
+ }
+ for (Head head : headMap.values()) {
+ List list = head.getHeadNameList();
+ if (list != null && !list.isEmpty() && list.size() < headRowNumber) {
+ int lack = headRowNumber - list.size();
+ int last = list.size() - 1;
+ for (int i = 0; i < lack; i++) {
+ list.add(list.get(last));
+ }
+ }
+ }
+ }
+
+ private void initColumnProperties(ConfigurationHolder configurationHolder) {
+ if (headClazz == null) {
+ return;
+ }
+ FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder);
+
+ for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) {
+ initOneColumnProperty(entry.getKey(), entry.getValue(),
+ fieldCache.getIndexFieldMap().containsKey(entry.getKey()));
+ }
+ headKind = HeadKindEnum.CLASS;
+ }
+
+ /**
+ * Initialization column property
+ *
+ * @param index
+ * @param field
+ * @param forceIndex
+ * @return Ignore current field
+ */
+ private void initOneColumnProperty(int index, FieldWrapper field, Boolean forceIndex) {
+ List tmpHeadList = new ArrayList<>();
+ boolean notForceName = field.getHeads() == null || field.getHeads().length == 0
+ || (field.getHeads().length == 1 && StringUtils.isEmpty(field.getHeads()[0]));
+ if (headMap.containsKey(index)) {
+ tmpHeadList.addAll(headMap.get(index).getHeadNameList());
+ } else {
+ if (notForceName) {
+ tmpHeadList.add(field.getFieldName());
+ } else {
+ Collections.addAll(tmpHeadList, field.getHeads());
+ }
+ }
+ Head head = new Head(index, field.getField(), field.getFieldName(), tmpHeadList, forceIndex, !notForceName);
+ headMap.put(index, head);
+ }
+
+ public boolean hasHead() {
+ return headKind != HeadKindEnum.NONE;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/FontProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/FontProperty.java
new file mode 100644
index 0000000..da343d0
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/FontProperty.java
@@ -0,0 +1,140 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.ContentFontStyle;
+import ai.chat2db.excel.annotation.write.style.HeadFontStyle;
+import ai.chat2db.excel.util.StringUtils;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+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;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class FontProperty {
+ /**
+ * The name for the font (i.e. Arial)
+ */
+ private String fontName;
+ /**
+ * Height in the familiar unit of measure - points
+ */
+ private Short fontHeightInPoints;
+ /**
+ * Whether to use italics or not
+ */
+ private Boolean italic;
+ /**
+ * Whether to use a strikeout horizontal line through the text or not
+ */
+ private Boolean strikeout;
+ /**
+ * The color for the font
+ *
+ * @see Font#COLOR_NORMAL
+ * @see Font#COLOR_RED
+ * @see HSSFPalette#getColor(short)
+ * @see IndexedColors
+ */
+ private Short color;
+ /**
+ * Set normal, super or subscript.
+ *
+ * @see Font#SS_NONE
+ * @see Font#SS_SUPER
+ * @see Font#SS_SUB
+ */
+ private Short typeOffset;
+ /**
+ * set type of text underlining to use
+ *
+ * @see Font#U_NONE
+ * @see Font#U_SINGLE
+ * @see Font#U_DOUBLE
+ * @see Font#U_SINGLE_ACCOUNTING
+ * @see Font#U_DOUBLE_ACCOUNTING
+ */
+
+ private Byte underline;
+ /**
+ * Set character-set to use.
+ *
+ * @see FontCharset
+ * @see Font#ANSI_CHARSET
+ * @see Font#DEFAULT_CHARSET
+ * @see Font#SYMBOL_CHARSET
+ */
+ private Integer charset;
+ /**
+ * Bold
+ */
+ private Boolean bold;
+
+ public static FontProperty build(HeadFontStyle headFontStyle) {
+ if (headFontStyle == null) {
+ return null;
+ }
+ FontProperty styleProperty = new FontProperty();
+ if (StringUtils.isNotBlank(headFontStyle.fontName())) {
+ styleProperty.setFontName(headFontStyle.fontName());
+ }
+ if (headFontStyle.fontHeightInPoints() >= 0) {
+ styleProperty.setFontHeightInPoints(headFontStyle.fontHeightInPoints());
+ }
+ styleProperty.setItalic(headFontStyle.italic().getBooleanValue());
+ styleProperty.setStrikeout(headFontStyle.strikeout().getBooleanValue());
+ if (headFontStyle.color() >= 0) {
+ styleProperty.setColor(headFontStyle.color());
+ }
+ if (headFontStyle.typeOffset() >= 0) {
+ styleProperty.setTypeOffset(headFontStyle.typeOffset());
+ }
+ if (headFontStyle.underline() >= 0) {
+ styleProperty.setUnderline(headFontStyle.underline());
+ }
+ if (headFontStyle.charset() >= 0) {
+ styleProperty.setCharset(headFontStyle.charset());
+ }
+ styleProperty.setBold(headFontStyle.bold().getBooleanValue());
+ return styleProperty;
+ }
+
+ public static FontProperty build(ContentFontStyle contentFontStyle) {
+ if (contentFontStyle == null) {
+ return null;
+ }
+ FontProperty styleProperty = new FontProperty();
+ if (StringUtils.isNotBlank(contentFontStyle.fontName())) {
+ styleProperty.setFontName(contentFontStyle.fontName());
+ }
+ if (contentFontStyle.fontHeightInPoints() >= 0) {
+ styleProperty.setFontHeightInPoints(contentFontStyle.fontHeightInPoints());
+ }
+ styleProperty.setItalic(contentFontStyle.italic().getBooleanValue());
+ styleProperty.setStrikeout(contentFontStyle.strikeout().getBooleanValue());
+ if (contentFontStyle.color() >= 0) {
+ styleProperty.setColor(contentFontStyle.color());
+ }
+ if (contentFontStyle.typeOffset() >= 0) {
+ styleProperty.setTypeOffset(contentFontStyle.typeOffset());
+ }
+ if (contentFontStyle.underline() >= 0) {
+ styleProperty.setUnderline(contentFontStyle.underline());
+ }
+ if (contentFontStyle.charset() >= 0) {
+ styleProperty.setCharset(contentFontStyle.charset());
+ }
+ styleProperty.setBold(contentFontStyle.bold().getBooleanValue());
+ return styleProperty;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/LoopMergeProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/LoopMergeProperty.java
new file mode 100644
index 0000000..c33ed39
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/LoopMergeProperty.java
@@ -0,0 +1,47 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.ContentLoopMerge;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+public class LoopMergeProperty {
+ /**
+ * Each row
+ */
+ private int eachRow;
+ /**
+ * Extend column
+ */
+ private int columnExtend;
+
+ public LoopMergeProperty(int eachRow, int columnExtend) {
+ this.eachRow = eachRow;
+ this.columnExtend = columnExtend;
+ }
+
+ public static LoopMergeProperty build(ContentLoopMerge contentLoopMerge) {
+ if (contentLoopMerge == null) {
+ return null;
+ }
+ return new LoopMergeProperty(contentLoopMerge.eachRow(), contentLoopMerge.columnExtend());
+ }
+
+ public int getEachRow() {
+ return eachRow;
+ }
+
+ public void setEachRow(int eachRow) {
+ this.eachRow = eachRow;
+ }
+
+ public int getColumnExtend() {
+ return columnExtend;
+ }
+
+ public void setColumnExtend(int columnExtend) {
+ this.columnExtend = columnExtend;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/NumberFormatProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/NumberFormatProperty.java
new file mode 100644
index 0000000..b17573a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/NumberFormatProperty.java
@@ -0,0 +1,43 @@
+package ai.chat2db.excel.metadata.property;
+
+import java.math.RoundingMode;
+
+import ai.chat2db.excel.annotation.format.NumberFormat;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+public class NumberFormatProperty {
+ private String format;
+ private RoundingMode roundingMode;
+
+ public NumberFormatProperty(String format, RoundingMode roundingMode) {
+ this.format = format;
+ this.roundingMode = roundingMode;
+ }
+
+ public static NumberFormatProperty build(NumberFormat numberFormat) {
+ if (numberFormat == null) {
+ return null;
+ }
+ return new NumberFormatProperty(numberFormat.value(), numberFormat.roundingMode());
+ }
+
+ public String getFormat() {
+ return format;
+ }
+
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+ public RoundingMode getRoundingMode() {
+ return roundingMode;
+ }
+
+ public void setRoundingMode(RoundingMode roundingMode) {
+ this.roundingMode = roundingMode;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/OnceAbsoluteMergeProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/OnceAbsoluteMergeProperty.java
new file mode 100644
index 0000000..4b5e6f1
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/OnceAbsoluteMergeProperty.java
@@ -0,0 +1,74 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.OnceAbsoluteMerge;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+public class OnceAbsoluteMergeProperty {
+ /**
+ * First row
+ */
+ private int firstRowIndex;
+ /**
+ * Last row
+ */
+ private int lastRowIndex;
+ /**
+ * First column
+ */
+ private int firstColumnIndex;
+ /**
+ * Last row
+ */
+ private int lastColumnIndex;
+
+ public OnceAbsoluteMergeProperty(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) {
+ this.firstRowIndex = firstRowIndex;
+ this.lastRowIndex = lastRowIndex;
+ this.firstColumnIndex = firstColumnIndex;
+ this.lastColumnIndex = lastColumnIndex;
+ }
+
+ public static OnceAbsoluteMergeProperty build(OnceAbsoluteMerge onceAbsoluteMerge) {
+ if (onceAbsoluteMerge == null) {
+ return null;
+ }
+ return new OnceAbsoluteMergeProperty(onceAbsoluteMerge.firstRowIndex(), onceAbsoluteMerge.lastRowIndex(),
+ onceAbsoluteMerge.firstColumnIndex(), onceAbsoluteMerge.lastColumnIndex());
+ }
+
+ public int getFirstRowIndex() {
+ return firstRowIndex;
+ }
+
+ public void setFirstRowIndex(int firstRowIndex) {
+ this.firstRowIndex = firstRowIndex;
+ }
+
+ public int getLastRowIndex() {
+ return lastRowIndex;
+ }
+
+ public void setLastRowIndex(int lastRowIndex) {
+ this.lastRowIndex = lastRowIndex;
+ }
+
+ public int getFirstColumnIndex() {
+ return firstColumnIndex;
+ }
+
+ public void setFirstColumnIndex(int firstColumnIndex) {
+ this.firstColumnIndex = firstColumnIndex;
+ }
+
+ public int getLastColumnIndex() {
+ return lastColumnIndex;
+ }
+
+ public void setLastColumnIndex(int lastColumnIndex) {
+ this.lastColumnIndex = lastColumnIndex;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/RowHeightProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/RowHeightProperty.java
new file mode 100644
index 0000000..78f1ee5
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/RowHeightProperty.java
@@ -0,0 +1,39 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.ContentRowHeight;
+import ai.chat2db.excel.annotation.write.style.HeadRowHeight;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+public class RowHeightProperty {
+ private Short height;
+
+ public RowHeightProperty(Short height) {
+ this.height = height;
+ }
+
+ public static RowHeightProperty build(HeadRowHeight headRowHeight) {
+ if (headRowHeight == null || headRowHeight.value() < 0) {
+ return null;
+ }
+ return new RowHeightProperty(headRowHeight.value());
+ }
+
+ public static RowHeightProperty build(ContentRowHeight contentRowHeight) {
+ if (contentRowHeight == null || contentRowHeight.value() < 0) {
+ return null;
+ }
+ return new RowHeightProperty(contentRowHeight.value());
+ }
+
+ public Short getHeight() {
+ return height;
+ }
+
+ public void setHeight(Short height) {
+ this.height = height;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/StyleProperty.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/StyleProperty.java
new file mode 100644
index 0000000..0c408a8
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/metadata/property/StyleProperty.java
@@ -0,0 +1,242 @@
+package ai.chat2db.excel.metadata.property;
+
+import ai.chat2db.excel.annotation.write.style.ContentStyle;
+import ai.chat2db.excel.annotation.write.style.HeadStyle;
+import ai.chat2db.excel.metadata.data.DataFormatData;
+import ai.chat2db.excel.write.metadata.style.WriteFont;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IgnoredErrorType;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+
+/**
+ * Configuration from annotations
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class StyleProperty {
+ /**
+ * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}.
+ */
+ private DataFormatData dataFormatData;
+ /**
+ * Set the font for this style
+ */
+ private WriteFont writeFont;
+ /**
+ * Set the cell's using this style to be hidden
+ */
+ private Boolean hidden;
+
+ /**
+ * Set the cell's using this style to be locked
+ */
+ private Boolean locked;
+ /**
+ * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which
+ * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see
+ * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel
+ */
+ private Boolean quotePrefix;
+ /**
+ * Set the type of horizontal alignment for the cell
+ */
+ private HorizontalAlignment horizontalAlignment;
+ /**
+ * Set whether the text should be wrapped. Setting this flag to true make all content visible within a
+ * cell by displaying it on multiple lines
+ */
+ private Boolean wrapped;
+ /**
+ * Set the type of vertical alignment for the cell
+ */
+ private VerticalAlignment verticalAlignment;
+ /**
+ * Set the degree of rotation for the text in the cell.
+ *
+ * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The
+ * implementations of this method will map between these two value-ranges accordingly, however the corresponding
+ * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is
+ * applied to.
+ */
+ private Short rotation;
+ /**
+ * Set the number of spaces to indent the text in the cell
+ */
+ private Short indent;
+ /**
+ * Set the type of border to use for the left border of the cell
+ */
+ private BorderStyle borderLeft;
+ /**
+ * Set the type of border to use for the right border of the cell
+ */
+ private BorderStyle borderRight;
+ /**
+ * Set the type of border to use for the top border of the cell
+ */
+ private BorderStyle borderTop;
+
+ /**
+ * Set the type of border to use for the bottom border of the cell
+ */
+ private BorderStyle borderBottom;
+ /**
+ * Set the color to use for the left border
+ *
+ * @see IndexedColors
+ */
+ private Short leftBorderColor;
+
+ /**
+ * Set the color to use for the right border
+ *
+ * @see IndexedColors
+ */
+ private Short rightBorderColor;
+
+ /**
+ * Set the color to use for the top border
+ *
+ * @see IndexedColors
+ */
+ private Short topBorderColor;
+ /**
+ * Set the color to use for the bottom border
+ *
+ * @see IndexedColors
+ */
+ private Short bottomBorderColor;
+ /**
+ * Setting to one fills the cell with the foreground color... No idea about other values
+ *
+ * @see FillPatternType#SOLID_FOREGROUND
+ */
+ private FillPatternType fillPatternType;
+
+ /**
+ * Set the background fill color.
+ *
+ * @see IndexedColors
+ */
+ private Short fillBackgroundColor;
+
+ /**
+ * Set the foreground fill color Note: Ensure Foreground color is set prior to background color.
+ *
+ * @see IndexedColors
+ */
+ private Short fillForegroundColor;
+ /**
+ * Controls if the Cell should be auto-sized to shrink to fit if the text is too long
+ */
+ private Boolean shrinkToFit;
+
+ public static StyleProperty build(HeadStyle headStyle) {
+ if (headStyle == null) {
+ return null;
+ }
+ StyleProperty styleProperty = new StyleProperty();
+ if (headStyle.dataFormat() >= 0) {
+ DataFormatData dataFormatData = new DataFormatData();
+ dataFormatData.setIndex(headStyle.dataFormat());
+ styleProperty.setDataFormatData(dataFormatData);
+ }
+ styleProperty.setHidden(headStyle.hidden().getBooleanValue());
+ styleProperty.setLocked(headStyle.locked().getBooleanValue());
+ styleProperty.setQuotePrefix(headStyle.quotePrefix().getBooleanValue());
+ styleProperty.setHorizontalAlignment(headStyle.horizontalAlignment().getPoiHorizontalAlignment());
+ styleProperty.setWrapped(headStyle.wrapped().getBooleanValue());
+ styleProperty.setVerticalAlignment(headStyle.verticalAlignment().getPoiVerticalAlignmentEnum());
+ if (headStyle.rotation() >= 0) {
+ styleProperty.setRotation(headStyle.rotation());
+ }
+ if (headStyle.indent() >= 0) {
+ styleProperty.setIndent(headStyle.indent());
+ }
+ styleProperty.setBorderLeft(headStyle.borderLeft().getPoiBorderStyle());
+ styleProperty.setBorderRight(headStyle.borderRight().getPoiBorderStyle());
+ styleProperty.setBorderTop(headStyle.borderTop().getPoiBorderStyle());
+ styleProperty.setBorderBottom(headStyle.borderBottom().getPoiBorderStyle());
+ if (headStyle.leftBorderColor() >= 0) {
+ styleProperty.setLeftBorderColor(headStyle.leftBorderColor());
+ }
+ if (headStyle.rightBorderColor() >= 0) {
+ styleProperty.setRightBorderColor(headStyle.rightBorderColor());
+ }
+ if (headStyle.topBorderColor() >= 0) {
+ styleProperty.setTopBorderColor(headStyle.topBorderColor());
+ }
+ if (headStyle.bottomBorderColor() >= 0) {
+ styleProperty.setBottomBorderColor(headStyle.bottomBorderColor());
+ }
+ styleProperty.setFillPatternType(headStyle.fillPatternType().getPoiFillPatternType());
+ if (headStyle.fillBackgroundColor() >= 0) {
+ styleProperty.setFillBackgroundColor(headStyle.fillBackgroundColor());
+ }
+ if (headStyle.fillForegroundColor() >= 0) {
+ styleProperty.setFillForegroundColor(headStyle.fillForegroundColor());
+ }
+ styleProperty.setShrinkToFit(headStyle.shrinkToFit().getBooleanValue());
+ return styleProperty;
+ }
+
+ public static StyleProperty build(ContentStyle contentStyle) {
+ if (contentStyle == null) {
+ return null;
+ }
+ StyleProperty styleProperty = new StyleProperty();
+ if (contentStyle.dataFormat() >= 0) {
+ DataFormatData dataFormatData = new DataFormatData();
+ dataFormatData.setIndex(contentStyle.dataFormat());
+ styleProperty.setDataFormatData(dataFormatData);
+ }
+ styleProperty.setHidden(contentStyle.hidden().getBooleanValue());
+ styleProperty.setLocked(contentStyle.locked().getBooleanValue());
+ styleProperty.setQuotePrefix(contentStyle.quotePrefix().getBooleanValue());
+ styleProperty.setHorizontalAlignment(contentStyle.horizontalAlignment().getPoiHorizontalAlignment());
+ styleProperty.setWrapped(contentStyle.wrapped().getBooleanValue());
+ styleProperty.setVerticalAlignment(contentStyle.verticalAlignment().getPoiVerticalAlignmentEnum());
+ if (contentStyle.rotation() >= 0) {
+ styleProperty.setRotation(contentStyle.rotation());
+ }
+ if (contentStyle.indent() >= 0) {
+ styleProperty.setIndent(contentStyle.indent());
+ }
+ styleProperty.setBorderLeft(contentStyle.borderLeft().getPoiBorderStyle());
+ styleProperty.setBorderRight(contentStyle.borderRight().getPoiBorderStyle());
+ styleProperty.setBorderTop(contentStyle.borderTop().getPoiBorderStyle());
+ styleProperty.setBorderBottom(contentStyle.borderBottom().getPoiBorderStyle());
+ if (contentStyle.leftBorderColor() >= 0) {
+ styleProperty.setLeftBorderColor(contentStyle.leftBorderColor());
+ }
+ if (contentStyle.rightBorderColor() >= 0) {
+ styleProperty.setRightBorderColor(contentStyle.rightBorderColor());
+ }
+ if (contentStyle.topBorderColor() >= 0) {
+ styleProperty.setTopBorderColor(contentStyle.topBorderColor());
+ }
+ if (contentStyle.bottomBorderColor() >= 0) {
+ styleProperty.setBottomBorderColor(contentStyle.bottomBorderColor());
+ }
+ styleProperty.setFillPatternType(contentStyle.fillPatternType().getPoiFillPatternType());
+ if (contentStyle.fillBackgroundColor() >= 0) {
+ styleProperty.setFillBackgroundColor(contentStyle.fillBackgroundColor());
+ }
+ if (contentStyle.fillForegroundColor() >= 0) {
+ styleProperty.setFillForegroundColor(contentStyle.fillForegroundColor());
+ }
+ styleProperty.setShrinkToFit(contentStyle.shrinkToFit().getBooleanValue());
+ return styleProperty;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/AbstractExcelReaderParameterBuilder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/AbstractExcelReaderParameterBuilder.java
new file mode 100644
index 0000000..7710a06
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/AbstractExcelReaderParameterBuilder.java
@@ -0,0 +1,59 @@
+package ai.chat2db.excel.read.builder;
+
+import ai.chat2db.excel.read.listener.ReadListener;
+import ai.chat2db.excel.read.metadata.ReadBasicParameter;
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.metadata.AbstractParameterBuilder;
+
+/**
+ * Build ExcelBuilder
+ *
+ * @author Jiaju Zhuang
+ */
+public abstract class AbstractExcelReaderParameterBuilder extends AbstractParameterBuilder {
+ /**
+ * Count the number of added heads when read sheet.
+ *
+ *
+ * 0 - This Sheet has no head ,since the first row are the data
+ *
+ * 1 - This Sheet has one row head , this is the default
+ *
+ * 2 - This Sheet has two row head ,since the third row is the data
+ *
+ * @param headRowNumber
+ * @return
+ */
+ public T headRowNumber(Integer headRowNumber) {
+ parameter().setHeadRowNumber(headRowNumber);
+ return self();
+ }
+
+ /**
+ * Whether to use scientific Format.
+ *
+ * default is false
+ *
+ * @param useScientificFormat
+ * @return
+ */
+ public T useScientificFormat(Boolean useScientificFormat) {
+ parameter().setUseScientificFormat(useScientificFormat);
+ return self();
+ }
+
+ /**
+ * Custom type listener run after default
+ *
+ * @param readListener
+ * @return
+ */
+ public T registerReadListener(ReadListener> readListener) {
+ if (parameter().getCustomReadListenerList() == null) {
+ parameter().setCustomReadListenerList(ListUtils.newArrayList());
+ }
+ parameter().getCustomReadListenerList().add(readListener);
+ return self();
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderBuilder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderBuilder.java
new file mode 100644
index 0000000..709e79e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderBuilder.java
@@ -0,0 +1,266 @@
+package ai.chat2db.excel.read.builder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import ai.chat2db.excel.cache.ReadCache;
+import ai.chat2db.excel.cache.selector.ReadCacheSelector;
+import ai.chat2db.excel.cache.selector.SimpleReadCacheSelector;
+import ai.chat2db.excel.enums.CellExtraTypeEnum;
+import ai.chat2db.excel.enums.ReadDefaultReturnEnum;
+import ai.chat2db.excel.event.AnalysisEventListener;
+import ai.chat2db.excel.event.SyncReadListener;
+import ai.chat2db.excel.read.listener.ModelBuildEventListener;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.ExcelReader;
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.support.ExcelTypeEnum;
+
+/**
+ * Build ExcelWriter
+ *
+ * @author Jiaju Zhuang
+ */
+public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder {
+ /**
+ * Workbook
+ */
+ private final ReadWorkbook readWorkbook;
+
+ public ExcelReaderBuilder() {
+ this.readWorkbook = new ReadWorkbook();
+ }
+
+ public ExcelReaderBuilder excelType(ExcelTypeEnum excelType) {
+ readWorkbook.setExcelType(excelType);
+ return this;
+ }
+
+ /**
+ * Read InputStream
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ public ExcelReaderBuilder file(InputStream inputStream) {
+ readWorkbook.setInputStream(inputStream);
+ return this;
+ }
+
+ /**
+ * Read file
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ public ExcelReaderBuilder file(File file) {
+ readWorkbook.setFile(file);
+ return this;
+ }
+
+ /**
+ * Read file
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ public ExcelReaderBuilder file(String pathName) {
+ return file(new File(pathName));
+ }
+
+ /**
+ * charset.
+ * Only work on the CSV file
+ */
+ public ExcelReaderBuilder charset(Charset charset) {
+ readWorkbook.setCharset(charset);
+ return this;
+ }
+
+ /**
+ * Mandatory use 'inputStream' .Default is false.
+ *
+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency
+ */
+ public ExcelReaderBuilder mandatoryUseInputStream(Boolean mandatoryUseInputStream) {
+ readWorkbook.setMandatoryUseInputStream(mandatoryUseInputStream);
+ return this;
+ }
+
+ /**
+ * Default true
+ *
+ * @param autoCloseStream
+ * @return
+ */
+ public ExcelReaderBuilder autoCloseStream(Boolean autoCloseStream) {
+ readWorkbook.setAutoCloseStream(autoCloseStream);
+ return this;
+ }
+
+ /**
+ * Ignore empty rows.Default is true.
+ *
+ * @param ignoreEmptyRow
+ * @return
+ */
+ public ExcelReaderBuilder ignoreEmptyRow(Boolean ignoreEmptyRow) {
+ readWorkbook.setIgnoreEmptyRow(ignoreEmptyRow);
+ return this;
+ }
+
+ /**
+ * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)}
+ * {@link AnalysisContext#getCustom()}
+ *
+ * @param customObject
+ * @return
+ */
+ public ExcelReaderBuilder customObject(Object customObject) {
+ readWorkbook.setCustomObject(customObject);
+ return this;
+ }
+
+ /**
+ * A cache that stores temp data to save memory.
+ *
+ * @param readCache
+ * @return
+ */
+ public ExcelReaderBuilder readCache(ReadCache readCache) {
+ readWorkbook.setReadCache(readCache);
+ return this;
+ }
+
+ /**
+ * Select the cache.Default use {@link SimpleReadCacheSelector}
+ *
+ * @param readCacheSelector
+ * @return
+ */
+ public ExcelReaderBuilder readCacheSelector(ReadCacheSelector readCacheSelector) {
+ readWorkbook.setReadCacheSelector(readCacheSelector);
+ return this;
+ }
+
+ /**
+ * Whether the encryption
+ *
+ * @param password
+ * @return
+ */
+ public ExcelReaderBuilder password(String password) {
+ readWorkbook.setPassword(password);
+ return this;
+ }
+
+ /**
+ * SAXParserFactory used when reading xlsx.
+ *
+ * The default will automatically find.
+ *
+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
+ *
+ * @param xlsxSAXParserFactoryName
+ * @return
+ * @see SAXParserFactory#newInstance()
+ * @see SAXParserFactory#newInstance(String, ClassLoader)
+ */
+ public ExcelReaderBuilder xlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) {
+ readWorkbook.setXlsxSAXParserFactoryName(xlsxSAXParserFactoryName);
+ return this;
+ }
+
+ /**
+ * Read some extra information, not by default
+ *
+ * @param extraType extra information type
+ * @return
+ */
+ public ExcelReaderBuilder extraRead(CellExtraTypeEnum extraType) {
+ if (readWorkbook.getExtraReadSet() == null) {
+ readWorkbook.setExtraReadSet(new HashSet());
+ }
+ readWorkbook.getExtraReadSet().add(extraType);
+ return this;
+ }
+
+ /**
+ * Whether to use the default listener, which is used by default.
+ *
+ * The {@link ModelBuildEventListener} is loaded by default to convert the object.
+ *
+ * @param useDefaultListener
+ * @return
+ */
+ public ExcelReaderBuilder useDefaultListener(Boolean useDefaultListener) {
+ readWorkbook.setUseDefaultListener(useDefaultListener);
+ return this;
+ }
+
+ /**
+ * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
+ * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
+ *
+ * @see ReadDefaultReturnEnum
+ */
+ public ExcelReaderBuilder readDefaultReturn(ReadDefaultReturnEnum readDefaultReturn) {
+ readWorkbook.setReadDefaultReturn(readDefaultReturn);
+ return this;
+ }
+
+ public ExcelReader build() {
+ return new ExcelReader(readWorkbook);
+ }
+
+ public void doReadAll() {
+ try (ExcelReader excelReader = build()) {
+ excelReader.readAll();
+ }
+ }
+
+ /**
+ * Synchronous reads return results
+ *
+ * @return
+ */
+ public List doReadAllSync() {
+ SyncReadListener syncReadListener = new SyncReadListener();
+ registerReadListener(syncReadListener);
+ try (ExcelReader excelReader = build()) {
+ excelReader.readAll();
+ excelReader.finish();
+ }
+ return (List)syncReadListener.getList();
+ }
+
+ public ExcelReaderSheetBuilder sheet() {
+ return sheet(null, null);
+ }
+
+ public ExcelReaderSheetBuilder sheet(Integer sheetNo) {
+ return sheet(sheetNo, null);
+ }
+
+ public ExcelReaderSheetBuilder sheet(String sheetName) {
+ return sheet(null, sheetName);
+ }
+
+ public ExcelReaderSheetBuilder sheet(Integer sheetNo, String sheetName) {
+ ExcelReaderSheetBuilder excelReaderSheetBuilder = new ExcelReaderSheetBuilder(build());
+ if (sheetNo != null) {
+ excelReaderSheetBuilder.sheetNo(sheetNo);
+ }
+ if (sheetName != null) {
+ excelReaderSheetBuilder.sheetName(sheetName);
+ }
+ return excelReaderSheetBuilder;
+ }
+
+ @Override
+ protected ReadWorkbook parameter() {
+ return readWorkbook;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderSheetBuilder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderSheetBuilder.java
new file mode 100644
index 0000000..0df651a
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/builder/ExcelReaderSheetBuilder.java
@@ -0,0 +1,89 @@
+package ai.chat2db.excel.read.builder;
+
+import java.util.List;
+
+import ai.chat2db.excel.event.SyncReadListener;
+import ai.chat2db.excel.read.metadata.ReadSheet;
+import ai.chat2db.excel.ExcelReader;
+import ai.chat2db.excel.exception.ExcelAnalysisException;
+import ai.chat2db.excel.exception.ExcelGenerateException;
+
+/**
+ * Build sheet
+ *
+ * @author Jiaju Zhuang
+ */
+public class ExcelReaderSheetBuilder extends AbstractExcelReaderParameterBuilder {
+ private ExcelReader excelReader;
+ /**
+ * Sheet
+ */
+ private final ReadSheet readSheet;
+
+ public ExcelReaderSheetBuilder() {
+ this.readSheet = new ReadSheet();
+ }
+
+ public ExcelReaderSheetBuilder(ExcelReader excelReader) {
+ this.readSheet = new ReadSheet();
+ this.excelReader = excelReader;
+ }
+
+ /**
+ * Starting from 0
+ *
+ * @param sheetNo
+ * @return
+ */
+ public ExcelReaderSheetBuilder sheetNo(Integer sheetNo) {
+ readSheet.setSheetNo(sheetNo);
+ return this;
+ }
+
+ /**
+ * sheet name
+ *
+ * @param sheetName
+ * @return
+ */
+ public ExcelReaderSheetBuilder sheetName(String sheetName) {
+ readSheet.setSheetName(sheetName);
+ return this;
+ }
+
+ public ReadSheet build() {
+ return readSheet;
+ }
+
+ /**
+ * Sax read
+ */
+ public void doRead() {
+ if (excelReader == null) {
+ throw new ExcelGenerateException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
+ }
+ excelReader.read(build());
+ excelReader.finish();
+ }
+
+ /**
+ * Synchronous reads return results
+ *
+ * @return
+ */
+ public List doReadSync() {
+ if (excelReader == null) {
+ throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
+ }
+ SyncReadListener syncReadListener = new SyncReadListener();
+ registerReadListener(syncReadListener);
+ excelReader.read(build());
+ excelReader.finish();
+ return (List)syncReadListener.getList();
+ }
+
+ @Override
+ protected ReadSheet parameter() {
+ return readSheet;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/IgnoreExceptionReadListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/IgnoreExceptionReadListener.java
new file mode 100644
index 0000000..b1c7948
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/IgnoreExceptionReadListener.java
@@ -0,0 +1,23 @@
+package ai.chat2db.excel.read.listener;
+
+import ai.chat2db.excel.context.AnalysisContext;
+
+/**
+ * Interface to listen for read results
+ *
+ * @author Jiaju Zhuang
+ */
+public interface IgnoreExceptionReadListener extends ReadListener {
+
+ /**
+ * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
+ * entire read will terminate.
+ *
+ * @param exception
+ * @param context
+ * @throws Exception
+ */
+ @Override
+ default void onException(Exception exception, AnalysisContext context) throws Exception {}
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ModelBuildEventListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ModelBuildEventListener.java
new file mode 100644
index 0000000..0546f71
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ModelBuildEventListener.java
@@ -0,0 +1,158 @@
+package ai.chat2db.excel.read.listener;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+import ai.chat2db.excel.enums.CellDataTypeEnum;
+import ai.chat2db.excel.enums.HeadKindEnum;
+import ai.chat2db.excel.enums.ReadDefaultReturnEnum;
+import ai.chat2db.excel.read.metadata.holder.ReadSheetHolder;
+import ai.chat2db.excel.read.metadata.property.ExcelReadHeadProperty;
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.exception.ExcelDataConvertException;
+import ai.chat2db.excel.metadata.Head;
+import ai.chat2db.excel.metadata.data.DataFormatData;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+import ai.chat2db.excel.support.cglib.beans.BeanMap;
+import ai.chat2db.excel.util.BeanMapUtils;
+import ai.chat2db.excel.util.ClassUtils;
+import ai.chat2db.excel.util.ConverterUtils;
+import ai.chat2db.excel.util.DateUtils;
+import ai.chat2db.excel.util.MapUtils;
+
+
+/**
+ * Convert to the object the user needs
+ *
+ * @author jipengfei
+ */
+public class ModelBuildEventListener implements IgnoreExceptionReadListener>> {
+
+ @Override
+ public void invoke(Map> cellDataMap, AnalysisContext context) {
+ ReadSheetHolder readSheetHolder = context.readSheetHolder();
+ if (HeadKindEnum.CLASS.equals(readSheetHolder.excelReadHeadProperty().getHeadKind())) {
+ context.readRowHolder()
+ .setCurrentRowAnalysisResult(buildUserModel(cellDataMap, readSheetHolder, context));
+ return;
+ }
+ context.readRowHolder().setCurrentRowAnalysisResult(buildNoModel(cellDataMap, readSheetHolder, context));
+ }
+
+ private Object buildNoModel(Map> cellDataMap, ReadSheetHolder readSheetHolder,
+ AnalysisContext context) {
+ int index = 0;
+ Map map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size());
+ for (Map.Entry> entry : cellDataMap.entrySet()) {
+ Integer key = entry.getKey();
+ ReadCellData> cellData = entry.getValue();
+ while (index < key) {
+ map.put(index, null);
+ index++;
+ }
+ index++;
+
+ ReadDefaultReturnEnum readDefaultReturn = context.readWorkbookHolder().getReadDefaultReturn();
+ if (readDefaultReturn == ReadDefaultReturnEnum.STRING) {
+ // string
+ map.put(key,
+ (String) ConverterUtils.convertToJavaObject(cellData, null, null, readSheetHolder.converterMap(),
+ context, context.readRowHolder().getRowIndex(), key));
+ } else {
+ // retrun ReadCellData
+ ReadCellData> convertedReadCellData = convertReadCellData(cellData,
+ context.readWorkbookHolder().getReadDefaultReturn(), readSheetHolder, context, key);
+ if (readDefaultReturn == ReadDefaultReturnEnum.READ_CELL_DATA) {
+ map.put(key, convertedReadCellData);
+ } else {
+ map.put(key, convertedReadCellData.getData());
+ }
+ }
+ }
+ // fix https://github.com/alibaba/easyexcel/issues/2014
+ int headSize = calculateHeadSize(readSheetHolder);
+ while (index < headSize) {
+ map.put(index, null);
+ index++;
+ }
+ return map;
+ }
+
+ private ReadCellData convertReadCellData(ReadCellData> cellData, ReadDefaultReturnEnum readDefaultReturn,
+ ReadSheetHolder readSheetHolder, AnalysisContext context, Integer columnIndex) {
+ Class> classGeneric;
+ switch (cellData.getType()) {
+ case STRING:
+ case DIRECT_STRING:
+ case ERROR:
+ case EMPTY:
+ classGeneric = String.class;
+ break;
+ case BOOLEAN:
+ classGeneric = Boolean.class;
+ break;
+ case NUMBER:
+ DataFormatData dataFormatData = cellData.getDataFormatData();
+ if (dataFormatData != null && DateUtils.isADateFormat(dataFormatData.getIndex(),
+ dataFormatData.getFormat())) {
+ classGeneric = LocalDateTime.class;
+ } else {
+ classGeneric = BigDecimal.class;
+ }
+ break;
+ default:
+ classGeneric = ConverterUtils.defaultClassGeneric;
+ break;
+ }
+
+ return (ReadCellData)ConverterUtils.convertToJavaObject(cellData, null, ReadCellData.class,
+ classGeneric, null, readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(),
+ columnIndex);
+ }
+
+ private int calculateHeadSize(ReadSheetHolder readSheetHolder) {
+ if (readSheetHolder.excelReadHeadProperty().getHeadMap().size() > 0) {
+ return readSheetHolder.excelReadHeadProperty().getHeadMap().size();
+ }
+ if (readSheetHolder.getMaxNotEmptyDataHeadSize() != null) {
+ return readSheetHolder.getMaxNotEmptyDataHeadSize();
+ }
+ return 0;
+ }
+
+ private Object buildUserModel(Map> cellDataMap, ReadSheetHolder readSheetHolder,
+ AnalysisContext context) {
+ ExcelReadHeadProperty excelReadHeadProperty = readSheetHolder.excelReadHeadProperty();
+ Object resultModel;
+ try {
+ resultModel = excelReadHeadProperty.getHeadClazz().newInstance();
+ } catch (Exception e) {
+ throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0,
+ new ReadCellData<>(CellDataTypeEnum.EMPTY), null,
+ "Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);
+ }
+ Map headMap = excelReadHeadProperty.getHeadMap();
+ BeanMap dataMap = BeanMapUtils.create(resultModel);
+ for (Map.Entry entry : headMap.entrySet()) {
+ Integer index = entry.getKey();
+ Head head = entry.getValue();
+ String fieldName = head.getFieldName();
+ if (!cellDataMap.containsKey(index)) {
+ continue;
+ }
+ ReadCellData> cellData = cellDataMap.get(index);
+ Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(),
+ ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(),
+ fieldName, readSheetHolder), readSheetHolder.converterMap(), context,
+ context.readRowHolder().getRowIndex(), index);
+ if (value != null) {
+ dataMap.put(fieldName, value);
+ }
+ }
+ return resultModel;
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {}
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/PageReadListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/PageReadListener.java
new file mode 100644
index 0000000..b45a2b7
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/PageReadListener.java
@@ -0,0 +1,60 @@
+package ai.chat2db.excel.read.listener;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.context.AnalysisContext;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+/**
+ * page read listener
+ *
+ * @author Jiaju Zhuang
+ */
+public class PageReadListener implements ReadListener {
+ /**
+ * Default single handle the amount of data
+ */
+ public static int BATCH_COUNT = 100;
+ /**
+ * Temporary storage of data
+ */
+ private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ /**
+ * consumer
+ */
+ private final Consumer> consumer;
+
+ /**
+ * Single handle the amount of data
+ */
+ private final int batchCount;
+
+ public PageReadListener(Consumer> consumer) {
+ this(consumer, BATCH_COUNT);
+ }
+
+ public PageReadListener(Consumer> consumer, int batchCount) {
+ this.consumer = consumer;
+ this.batchCount = batchCount;
+ }
+
+ @Override
+ public void invoke(T data, AnalysisContext context) {
+ cachedDataList.add(data);
+ if (cachedDataList.size() >= batchCount) {
+ consumer.accept(cachedDataList);
+ cachedDataList = ListUtils.newArrayListWithExpectedSize(batchCount);
+ }
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ if (CollectionUtils.isNotEmpty(cachedDataList)) {
+ consumer.accept(cachedDataList);
+ }
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ReadListener.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ReadListener.java
new file mode 100644
index 0000000..37fa890
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/listener/ReadListener.java
@@ -0,0 +1,68 @@
+package ai.chat2db.excel.read.listener;
+
+import java.util.Map;
+
+import ai.chat2db.excel.event.Listener;
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.metadata.CellExtra;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+
+/**
+ * Interface to listen for read results
+ *
+ * @author Jiaju Zhuang
+ */
+public interface ReadListener extends Listener {
+ /**
+ * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
+ * entire read will terminate.
+ *
+ * @param exception
+ * @param context
+ * @throws Exception
+ */
+ default void onException(Exception exception, AnalysisContext context) throws Exception {
+ throw exception;
+ }
+
+ /**
+ * When analysis one head row trigger invoke function.
+ *
+ * @param headMap
+ * @param context
+ */
+ default void invokeHead(Map> headMap, AnalysisContext context) {}
+
+ /**
+ * When analysis one row trigger invoke function.
+ *
+ * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()}
+ * @param context analysis context
+ */
+ void invoke(T data, AnalysisContext context);
+
+ /**
+ * The current method is called when extra information is returned
+ *
+ * @param extra extra information
+ * @param context analysis context
+ */
+ default void extra(CellExtra extra, AnalysisContext context) {}
+
+ /**
+ * if have something to do after all analysis
+ *
+ * @param context
+ */
+ void doAfterAllAnalysed(AnalysisContext context);
+
+ /**
+ * Verify that there is another piece of data.You can stop the read by returning false
+ *
+ * @param context
+ * @return
+ */
+ default boolean hasNext(AnalysisContext context) {
+ return true;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadBasicParameter.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadBasicParameter.java
new file mode 100644
index 0000000..88208bd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadBasicParameter.java
@@ -0,0 +1,41 @@
+package ai.chat2db.excel.read.metadata;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ai.chat2db.excel.metadata.BasicParameter;
+import ai.chat2db.excel.read.listener.ReadListener;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Read basic parameter
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ReadBasicParameter extends BasicParameter {
+ /**
+ * Count the number of added heads when read sheet.
+ *
+ *
+ * 0 - This Sheet has no head ,since the first row are the data
+ *
+ * 1 - This Sheet has one row head , this is the default
+ *
+ * 2 - This Sheet has two row head ,since the third row is the data
+ */
+ private Integer headRowNumber;
+ /**
+ * Custom type listener run after default
+ */
+ private List> customReadListenerList;
+
+ public ReadBasicParameter() {
+ customReadListenerList = new ArrayList<>();
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadSheet.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadSheet.java
new file mode 100644
index 0000000..527c244
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadSheet.java
@@ -0,0 +1,62 @@
+package ai.chat2db.excel.read.metadata;
+
+/**
+ * Read sheet
+ *
+ * @author jipengfei
+ */
+public class ReadSheet extends ReadBasicParameter {
+ /**
+ * Starting from 0
+ */
+ private Integer sheetNo;
+ /**
+ * sheet name
+ */
+ private String sheetName;
+
+ public ReadSheet() {}
+
+ public ReadSheet(Integer sheetNo) {
+ this.sheetNo = sheetNo;
+ }
+
+ public ReadSheet(Integer sheetNo, String sheetName) {
+ this.sheetNo = sheetNo;
+ this.sheetName = sheetName;
+ }
+
+ public Integer getSheetNo() {
+ return sheetNo;
+ }
+
+ public void setSheetNo(Integer sheetNo) {
+ this.sheetNo = sheetNo;
+ }
+
+ public String getSheetName() {
+ return sheetName;
+ }
+
+ public void setSheetName(String sheetName) {
+ this.sheetName = sheetName;
+ }
+
+ public void copyBasicParameter(ReadSheet other) {
+ if (other == null) {
+ return;
+ }
+ this.setHeadRowNumber(other.getHeadRowNumber());
+ this.setCustomReadListenerList(other.getCustomReadListenerList());
+ this.setHead(other.getHead());
+ this.setClazz(other.getClazz());
+ this.setCustomConverterList(other.getCustomConverterList());
+ this.setAutoTrim(other.getAutoTrim());
+ this.setUse1904windowing(other.getUse1904windowing());
+ }
+
+ @Override
+ public String toString() {
+ return "ReadSheet{" + "sheetNo=" + sheetNo + ", sheetName='" + sheetName + '\'' + "} " + super.toString();
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadWorkbook.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadWorkbook.java
new file mode 100644
index 0000000..9653077
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/ReadWorkbook.java
@@ -0,0 +1,118 @@
+package ai.chat2db.excel.read.metadata;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Set;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import ai.chat2db.excel.cache.ReadCache;
+import ai.chat2db.excel.cache.selector.ReadCacheSelector;
+import ai.chat2db.excel.cache.selector.SimpleReadCacheSelector;
+import ai.chat2db.excel.enums.CellExtraTypeEnum;
+import ai.chat2db.excel.enums.ReadDefaultReturnEnum;
+import ai.chat2db.excel.event.AnalysisEventListener;
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.read.listener.ModelBuildEventListener;
+import ai.chat2db.excel.support.ExcelTypeEnum;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Workbook
+ *
+ * @author Jiaju Zhuang
+ **/
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ReadWorkbook extends ReadBasicParameter {
+ /**
+ * Excel type
+ */
+ private ExcelTypeEnum excelType;
+ /**
+ * Read InputStream
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ private InputStream inputStream;
+ /**
+ * Read file
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ private File file;
+ /**
+ * charset.
+ * Only work on the CSV file
+ */
+ private Charset charset;
+ /**
+ * Mandatory use 'inputStream' .Default is false.
+ *
+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency
+ */
+ private Boolean mandatoryUseInputStream;
+ /**
+ * Default true
+ */
+ private Boolean autoCloseStream;
+ /**
+ * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)}
+ * {@link AnalysisContext#getCustom()}
+ */
+ private Object customObject;
+ /**
+ * A cache that stores temp data to save memory.
+ */
+ private ReadCache readCache;
+ /**
+ * Ignore empty rows.Default is true.
+ */
+ private Boolean ignoreEmptyRow;
+ /**
+ * Select the cache.Default use {@link SimpleReadCacheSelector}
+ */
+ private ReadCacheSelector readCacheSelector;
+ /**
+ * Whether the encryption
+ */
+ private String password;
+ /**
+ * SAXParserFactory used when reading xlsx.
+ *
+ * The default will automatically find.
+ *
+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"
+ *
+ * @see SAXParserFactory#newInstance()
+ * @see SAXParserFactory#newInstance(String, ClassLoader)
+ */
+ private String xlsxSAXParserFactoryName;
+ /**
+ * Whether to use the default listener, which is used by default.
+ *
+ * The {@link ModelBuildEventListener} is loaded by default to convert the object.
+ * defualt is true.
+ */
+ private Boolean useDefaultListener;
+
+ /**
+ * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
+ * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
+ *
+ * @see ReadDefaultReturnEnum
+ */
+ private ReadDefaultReturnEnum readDefaultReturn;
+
+ /**
+ * Read some additional fields. None are read by default.
+ *
+ * @see CellExtraTypeEnum
+ */
+ private Set extraReadSet;
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/AbstractReadHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/AbstractReadHolder.java
new file mode 100644
index 0000000..efba0b6
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/AbstractReadHolder.java
@@ -0,0 +1,122 @@
+package ai.chat2db.excel.read.metadata.holder;
+
+import java.util.HashMap;
+import java.util.List;
+
+import ai.chat2db.excel.enums.HolderEnum;
+import ai.chat2db.excel.read.metadata.property.ExcelReadHeadProperty;
+import ai.chat2db.excel.util.ListUtils;
+import ai.chat2db.excel.converters.Converter;
+import ai.chat2db.excel.converters.ConverterKeyBuild;
+import ai.chat2db.excel.converters.DefaultConverterLoader;
+import ai.chat2db.excel.metadata.AbstractHolder;
+import ai.chat2db.excel.read.listener.ModelBuildEventListener;
+import ai.chat2db.excel.read.listener.ReadListener;
+import ai.chat2db.excel.read.metadata.ReadBasicParameter;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Read Holder
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public abstract class AbstractReadHolder extends AbstractHolder implements ReadHolder {
+ /**
+ * Count the number of added heads when read sheet.
+ *
+ *
+ * 0 - This Sheet has no head ,since the first row are the data
+ *
+ * 1 - This Sheet has one row head , this is the default
+ *
+ * 2 - This Sheet has two row head ,since the third row is the data
+ */
+ private Integer headRowNumber;
+ /**
+ * Excel head property
+ */
+ private ExcelReadHeadProperty excelReadHeadProperty;
+ /**
+ * Read listener
+ */
+ private List> readListenerList;
+
+ public AbstractReadHolder(ReadBasicParameter readBasicParameter, AbstractReadHolder parentAbstractReadHolder) {
+ super(readBasicParameter, parentAbstractReadHolder);
+
+ if (readBasicParameter.getUseScientificFormat() == null) {
+ if (parentAbstractReadHolder != null) {
+ getGlobalConfiguration().setUseScientificFormat(
+ parentAbstractReadHolder.getGlobalConfiguration().getUseScientificFormat());
+ }
+ } else {
+ getGlobalConfiguration().setUseScientificFormat(readBasicParameter.getUseScientificFormat());
+ }
+
+ // Initialization property
+ this.excelReadHeadProperty = new ExcelReadHeadProperty(this, getClazz(), getHead());
+ if (readBasicParameter.getHeadRowNumber() == null) {
+ if (parentAbstractReadHolder == null) {
+ if (excelReadHeadProperty.hasHead()) {
+ this.headRowNumber = excelReadHeadProperty.getHeadRowNumber();
+ } else {
+ this.headRowNumber = 1;
+ }
+ } else {
+ this.headRowNumber = parentAbstractReadHolder.getHeadRowNumber();
+ }
+ } else {
+ this.headRowNumber = readBasicParameter.getHeadRowNumber();
+ }
+
+ if (parentAbstractReadHolder == null) {
+ this.readListenerList = ListUtils.newArrayList();
+ } else {
+ this.readListenerList = ListUtils.newArrayList(parentAbstractReadHolder.getReadListenerList());
+ }
+ if (HolderEnum.WORKBOOK.equals(holderType())) {
+ Boolean useDefaultListener = ((ReadWorkbook)readBasicParameter).getUseDefaultListener();
+ if (useDefaultListener == null || useDefaultListener) {
+ readListenerList.add(new ModelBuildEventListener());
+ }
+ }
+ if (readBasicParameter.getCustomReadListenerList() != null
+ && !readBasicParameter.getCustomReadListenerList().isEmpty()) {
+ this.readListenerList.addAll(readBasicParameter.getCustomReadListenerList());
+ }
+
+ if (parentAbstractReadHolder == null) {
+ setConverterMap(DefaultConverterLoader.loadDefaultReadConverter());
+ } else {
+ setConverterMap(new HashMap<>(parentAbstractReadHolder.getConverterMap()));
+ }
+ if (readBasicParameter.getCustomConverterList() != null
+ && !readBasicParameter.getCustomConverterList().isEmpty()) {
+ for (Converter> converter : readBasicParameter.getCustomConverterList()) {
+ getConverterMap().put(
+ ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
+ converter);
+ }
+ }
+ }
+
+ @Override
+ public List> readListenerList() {
+ return getReadListenerList();
+ }
+
+ @Override
+ public ExcelReadHeadProperty excelReadHeadProperty() {
+ return getExcelReadHeadProperty();
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadHolder.java
new file mode 100644
index 0000000..e26f1fd
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadHolder.java
@@ -0,0 +1,30 @@
+package ai.chat2db.excel.read.metadata.holder;
+
+import java.util.List;
+
+import ai.chat2db.excel.read.metadata.property.ExcelReadHeadProperty;
+import ai.chat2db.excel.metadata.ConfigurationHolder;
+import ai.chat2db.excel.read.listener.ReadListener;
+
+/**
+ *
+ * Get the corresponding Holder
+ *
+ * @author Jiaju Zhuang
+ **/
+public interface ReadHolder extends ConfigurationHolder {
+ /**
+ * What handler does the currently operated cell need to execute
+ *
+ * @return Current {@link ReadListener}
+ */
+ List> readListenerList();
+
+ /**
+ * What {@link ExcelReadHeadProperty} does the currently operated cell need to execute
+ *
+ * @return Current {@link ExcelReadHeadProperty}
+ */
+ ExcelReadHeadProperty excelReadHeadProperty();
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadRowHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadRowHolder.java
new file mode 100644
index 0000000..9263d6e
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadRowHolder.java
@@ -0,0 +1,90 @@
+package ai.chat2db.excel.read.metadata.holder;
+
+import java.util.Map;
+
+import ai.chat2db.excel.enums.HolderEnum;
+import ai.chat2db.excel.enums.RowTypeEnum;
+import ai.chat2db.excel.metadata.Cell;
+import ai.chat2db.excel.metadata.GlobalConfiguration;
+import ai.chat2db.excel.metadata.Holder;
+
+/**
+ * sheet holder
+ *
+ * @author Jiaju Zhuang
+ */
+public class ReadRowHolder implements Holder {
+ /**
+ * Returns row index of a row in the sheet that contains this cell.Start form 0.
+ */
+ private Integer rowIndex;
+ /**
+ * Row type
+ */
+ private RowTypeEnum rowType;
+ /**
+ * Cell map
+ */
+ private Map cellMap;
+ /**
+ * The result of the previous listener
+ */
+ private Object currentRowAnalysisResult;
+ /**
+ * Some global variables
+ */
+ private GlobalConfiguration globalConfiguration;
+
+ public ReadRowHolder(Integer rowIndex, RowTypeEnum rowType, GlobalConfiguration globalConfiguration,
+ Map cellMap) {
+ this.rowIndex = rowIndex;
+ this.rowType = rowType;
+ this.globalConfiguration = globalConfiguration;
+ this.cellMap = cellMap;
+ }
+
+ public GlobalConfiguration getGlobalConfiguration() {
+ return globalConfiguration;
+ }
+
+ public void setGlobalConfiguration(GlobalConfiguration globalConfiguration) {
+ this.globalConfiguration = globalConfiguration;
+ }
+
+ public Object getCurrentRowAnalysisResult() {
+ return currentRowAnalysisResult;
+ }
+
+ public void setCurrentRowAnalysisResult(Object currentRowAnalysisResult) {
+ this.currentRowAnalysisResult = currentRowAnalysisResult;
+ }
+
+ public Integer getRowIndex() {
+ return rowIndex;
+ }
+
+ public void setRowIndex(Integer rowIndex) {
+ this.rowIndex = rowIndex;
+ }
+
+ public RowTypeEnum getRowType() {
+ return rowType;
+ }
+
+ public void setRowType(RowTypeEnum rowType) {
+ this.rowType = rowType;
+ }
+
+ public Map getCellMap() {
+ return cellMap;
+ }
+
+ public void setCellMap(Map cellMap) {
+ this.cellMap = cellMap;
+ }
+
+ @Override
+ public HolderEnum holderType() {
+ return HolderEnum.ROW;
+ }
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadSheetHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadSheetHolder.java
new file mode 100644
index 0000000..1e9da5f
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadSheetHolder.java
@@ -0,0 +1,101 @@
+package ai.chat2db.excel.read.metadata.holder;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import ai.chat2db.excel.enums.HolderEnum;
+import ai.chat2db.excel.read.metadata.ReadSheet;
+import ai.chat2db.excel.metadata.Cell;
+import ai.chat2db.excel.metadata.CellExtra;
+import ai.chat2db.excel.metadata.data.ReadCellData;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * sheet holder
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public class ReadSheetHolder extends AbstractReadHolder {
+
+ /**
+ * current param
+ */
+ private ReadSheet readSheet;
+ /***
+ * parent
+ */
+ private ReadWorkbookHolder parentReadWorkbookHolder;
+ /***
+ * sheetNo
+ */
+ private Integer sheetNo;
+ /***
+ * sheetName
+ */
+ private String sheetName;
+ /**
+ * Gets the total number of rows , data may be inaccurate
+ */
+ private Integer approximateTotalRowNumber;
+ /**
+ * Data storage of the current row.
+ */
+ private Map cellMap;
+ /**
+ * Data storage of the current extra cell.
+ */
+ private CellExtra cellExtra;
+ /**
+ * Index of the current row.
+ */
+ private Integer rowIndex;
+ /**
+ * Current CellData
+ */
+ private ReadCellData> tempCellData;
+ /**
+ * Read the size of the largest head in sheet head data.
+ * see https://github.com/alibaba/easyexcel/issues/2014
+ */
+ private Integer maxNotEmptyDataHeadSize;
+
+ /**
+ * Reading this sheet has ended.
+ */
+ private Boolean ended;
+
+ public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) {
+ super(readSheet, readWorkbookHolder);
+ this.readSheet = readSheet;
+ this.parentReadWorkbookHolder = readWorkbookHolder;
+ this.sheetNo = readSheet.getSheetNo();
+ this.sheetName = readSheet.getSheetName();
+ this.cellMap = new LinkedHashMap<>();
+ this.rowIndex = -1;
+ }
+
+ /**
+ * Approximate total number of rows.
+ * use: getApproximateTotalRowNumber()
+ *
+ * @return
+ */
+ @Deprecated
+ public Integer getTotal() {
+ return approximateTotalRowNumber;
+ }
+
+ @Override
+ public HolderEnum holderType() {
+ return HolderEnum.SHEET;
+ }
+
+}
diff --git a/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadWorkbookHolder.java b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadWorkbookHolder.java
new file mode 100644
index 0000000..317aa22
--- /dev/null
+++ b/easyexcel-plus-core/src/main/java/ai/chat2db/excel/read/metadata/holder/ReadWorkbookHolder.java
@@ -0,0 +1,197 @@
+package ai.chat2db.excel.read.metadata.holder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import ai.chat2db.excel.cache.ReadCache;
+import ai.chat2db.excel.cache.selector.EternalReadCacheSelector;
+import ai.chat2db.excel.cache.selector.ReadCacheSelector;
+import ai.chat2db.excel.cache.selector.SimpleReadCacheSelector;
+import ai.chat2db.excel.enums.CellExtraTypeEnum;
+import ai.chat2db.excel.enums.HolderEnum;
+import ai.chat2db.excel.enums.ReadDefaultReturnEnum;
+import ai.chat2db.excel.event.AnalysisEventListener;
+import ai.chat2db.excel.read.metadata.ReadSheet;
+import ai.chat2db.excel.context.AnalysisContext;
+import ai.chat2db.excel.exception.ExcelAnalysisException;
+import ai.chat2db.excel.read.metadata.ReadWorkbook;
+import ai.chat2db.excel.support.ExcelTypeEnum;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Workbook holder
+ *
+ * @author Jiaju Zhuang
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@NoArgsConstructor
+public class ReadWorkbookHolder extends AbstractReadHolder {
+
+ /**
+ * current param
+ */
+ private ReadWorkbook readWorkbook;
+ /**
+ * Read InputStream
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ private InputStream inputStream;
+ /**
+ * Read file
+ *
+ * If 'inputStream' and 'file' all not empty, file first
+ */
+ private File file;
+
+ /**
+ * charset.
+ * Only work on the CSV file
+ */
+ private Charset charset;
+ /**
+ * Mandatory use 'inputStream' .Default is false.
+ *
+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency
+ */
+ private Boolean mandatoryUseInputStream;
+
+ /**
+ * Default true
+ */
+ private Boolean autoCloseStream;
+
+ /**
+ * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
+ * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
+ *
+ * @see ReadDefaultReturnEnum
+ */
+ private ReadDefaultReturnEnum readDefaultReturn;
+
+ /**
+ * Excel type
+ */
+ private ExcelTypeEnum excelType;
+ /**
+ * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)}
+ * {@link AnalysisContext#getCustom()}
+ */
+ private Object customObject;
+ /**
+ * Ignore empty rows.Default is true.
+ */
+ private Boolean ignoreEmptyRow;
+ /**
+ * A cache that stores temp data to save memory.
+ */
+ private ReadCache readCache;
+ /**
+ * Select the cache.Default use {@link SimpleReadCacheSelector}
+ */
+ private ReadCacheSelector readCacheSelector;
+ /**
+ * Temporary files when reading excel
+ */
+ private File tempFile;
+ /**
+ * Whether the encryption
+ */
+ private String password;
+ /**
+ * Read some additional fields. None are read by default.
+ *
+ * @see CellExtraTypeEnum
+ */
+ private Set extraReadSet;
+ /**
+ * Actual sheet data
+ */
+ private List actualSheetDataList;
+ /**
+ * Parameter sheet data
+ */
+ private List parameterSheetDataList;
+ /**
+ * Read all
+ */
+ private Boolean readAll;
+
+ /**
+ * Prevent repeating sheet
+ */
+ private Set hasReadSheet;
+
+ public ReadWorkbookHolder(ReadWorkbook readWorkbook) {
+ super(readWorkbook, null);
+ this.readWorkbook = readWorkbook;
+ if (readWorkbook.getInputStream() != null) {
+ this.inputStream = readWorkbook.getInputStream();
+ }
+ this.file = readWorkbook.getFile();
+
+ if (readWorkbook.getCharset() == null) {
+ this.charset = Charset.defaultCharset();
+ } else {
+ this.charset = readWorkbook.getCharset();
+ }
+
+ if (readWorkbook.getMandatoryUseInputStream() == null) {
+ this.mandatoryUseInputStream = Boolean.FALSE;
+ } else {
+ this.mandatoryUseInputStream = readWorkbook.getMandatoryUseInputStream();
+ }
+ if (readWorkbook.getAutoCloseStream() == null) {
+ this.autoCloseStream = Boolean.TRUE;
+ } else {
+ this.autoCloseStream = readWorkbook.getAutoCloseStream();
+ }
+
+ if (readWorkbook.getReadDefaultReturn() == null) {
+ this.readDefaultReturn = ReadDefaultReturnEnum.STRING;
+ } else {
+ this.readDefaultReturn = readWorkbook.getReadDefaultReturn();
+ }
+
+ this.customObject = readWorkbook.getCustomObject();
+ if (readWorkbook.getIgnoreEmptyRow() == null) {
+ this.ignoreEmptyRow = Boolean.TRUE;
+ } else {
+ this.ignoreEmptyRow = readWorkbook.getIgnoreEmptyRow();
+ }
+ if (readWorkbook.getReadCache() != null) {
+ if (readWorkbook.getReadCacheSelector() != null) {
+ throw new ExcelAnalysisException("'readCache' and 'readCacheSelector' only one choice.");
+ }
+ this.readCacheSelector = new EternalReadCacheSelector(readWorkbook.getReadCache());
+ } else {
+ if (readWorkbook.getReadCacheSelector() == null) {
+ this.readCacheSelector = new SimpleReadCacheSelector();
+ } else {
+ this.readCacheSelector = readWorkbook.getReadCacheSelector();
+ }
+ }
+ if (readWorkbook.getExtraReadSet() == null) {
+ this.extraReadSet = new HashSet();
+ } else {
+ this.extraReadSet = readWorkbook.getExtraReadSet();
+ }
+ this.hasReadSheet = new HashSet | | | |