diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7c9de045e..cacb75ad0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -86,7 +86,7 @@ jobs:
restore-keys: |
${{ runner.os }}-m2
- name: Test with Maven
- run: ./mvnw clean package -B -Dmaven.test.skip=false -pl fesod-common,fesod-shaded,fesod-sheet
+ run: ./mvnw clean package -B -Dmaven.test.skip=false -pl fesod-common,fesod-shaded,fesod-sheet,fesod-examples/fesod-sheet-examples
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: (!cancelled())
diff --git a/fesod-examples/fesod-sheet-examples/pom.xml b/fesod-examples/fesod-sheet-examples/pom.xml
index 24c6b151b..3c01afb2b 100644
--- a/fesod-examples/fesod-sheet-examples/pom.xml
+++ b/fesod-examples/fesod-sheet-examples/pom.xml
@@ -33,15 +33,72 @@
jarFesod Sheet Examples
+
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Test.java
+ **/*ITCase.java
+
+
+ 1
+ false
+
+
+
+
+
+
org.apache.fesodfesod-sheet${project.version}
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback-classic.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+
+
org.springframework.bootspring-boot-starter-web
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-testtest
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/CustomConverterExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/CustomConverterExample.java
new file mode 100644
index 000000000..dc4e1f798
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/CustomConverterExample.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.advanced;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.examples.advanced.converter.CustomStringStringConverter;
+import org.apache.fesod.sheet.examples.advanced.data.CustomConverterData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.read.listener.ReadListener;
+
+/**
+ * Demonstrates registering a custom converter at the builder level for read and write.
+ *
+ *
Scenario
+ *
You need the same data transformation applied to ALL fields of a matching type,
+ * not just a specific annotated field. For example, adding a "Custom:" prefix to every
+ * string column, or encrypting/decrypting all string values.
+ *
+ *
Key Concepts: Per-Field vs. Global Converter Registration
{@code .registerConverter(new MyConverter())} on the builder
+ *
+ *
+ *
+ *
How It Works
+ *
+ *
Write: The {@link CustomStringStringConverter} is registered on the write builder.
+ * During write, every {@code String} field is transformed (prefixed with "Custom:").
+ *
Read: The same converter is registered on the read builder.
+ * During read, every string cell is transformed back through the converter.
{@link org.apache.fesod.sheet.examples.read.ConverterReadExample} — Per-field converter via annotation.
+ *
+ *
+ * @see CustomStringStringConverter
+ * @see org.apache.fesod.sheet.converters.Converter
+ */
+@Slf4j
+public class CustomConverterExample {
+
+ public static void main(String[] args) {
+ String fileName = ExampleFileUtil.getTempPath("customConverter" + System.currentTimeMillis() + ".xlsx");
+ customConverterWrite(fileName);
+ customConverterRead(fileName);
+ }
+
+ /**
+ * Writes data with a globally registered converter that prefixes all string values.
+ *
+ *
The {@link CustomStringStringConverter} transforms "String0" → "Custom:String0"
+ * for every string field in the data model.
+ */
+ public static void customConverterWrite(String fileName) {
+ FesodSheet.write(fileName, CustomConverterData.class)
+ .registerConverter(new CustomStringStringConverter())
+ .sheet("CustomConverter")
+ .doWrite(data());
+ log.info("Successfully wrote file with custom converter: {}", fileName);
+ }
+
+ /**
+ * Reads the previously written file with the same converter registered.
+ *
+ *
The converter's {@code convertToJavaData()} method is applied during read,
+ * transforming cell values as they are parsed.
+ */
+ public static void customConverterRead(String fileName) {
+ FesodSheet.read(fileName, CustomConverterData.class, new ReadListener() {
+ @Override
+ public void invoke(CustomConverterData data, AnalysisContext context) {
+ log.info("Read data with custom converter: {}", data);
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ log.info("Custom converter read completed");
+ }
+ })
+ .registerConverter(new CustomStringStringConverter())
+ .sheet()
+ .doRead();
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ CustomConverterData data = new CustomConverterData();
+ data.setString("String" + i);
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/LargeFileWriteExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/LargeFileWriteExample.java
new file mode 100644
index 000000000..d6bd78327
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/LargeFileWriteExample.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.advanced;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.ExcelWriter;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.examples.write.data.DemoData;
+import org.apache.fesod.sheet.util.FileUtils;
+import org.apache.fesod.sheet.write.handler.WorkbookWriteHandler;
+import org.apache.fesod.sheet.write.handler.context.WorkbookWriteHandlerContext;
+import org.apache.fesod.sheet.write.metadata.WriteSheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+
+/**
+ * Demonstrates writing very large Excel files (100,000+ rows) with memory optimization.
+ *
+ *
Scenario
+ *
You need to export a large dataset (e.g., database dump, log analysis) that would
+ * exhaust memory if all rows were held at once. Fesod uses Apache POI's streaming
+ * API (SXSSF) internally, but temporary XML files can consume significant disk space.
+ *
+ *
Key Optimization: Temporary File Compression
+ *
When POI writes large XLSX files, it creates temporary XML files on disk
+ * (one per sheet). These can be several times larger than the final file.
+ * Enabling compression via {@code setCompressTempFiles(true)} significantly reduces
+ * disk usage at the cost of slightly more CPU.
Uses a {@link WorkbookWriteHandler} to access the underlying POI
+ * {@link SXSSFWorkbook} and enable temp file compression. Writing is done
+ * in 1,000 batches of 100 rows each via the {@link ExcelWriter} API.
+ */
+ public static void compressedTemporaryFile() {
+ log.info("Temporary XML files are stored at: {}", FileUtils.getPoiFilesPath());
+ String fileName = ExampleFileUtil.getTempPath("largeFile" + System.currentTimeMillis() + ".xlsx");
+
+ try (ExcelWriter excelWriter = FesodSheet.write(fileName, DemoData.class)
+ .registerWriteHandler(new WorkbookWriteHandler() {
+ @Override
+ public void afterWorkbookCreate(WorkbookWriteHandlerContext context) {
+ Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
+ if (workbook instanceof SXSSFWorkbook) {
+ // Enable temporary file compression.
+ ((SXSSFWorkbook) workbook).setCompressTempFiles(true);
+ }
+ }
+ })
+ .build()) {
+ WriteSheet writeSheet = FesodSheet.writerSheet("Template").build();
+ // Write 100,000 rows in batches.
+ for (int i = 0; i < 1000; i++) {
+ excelWriter.write(data(), writeSheet);
+ }
+ }
+ log.info("Successfully wrote large file: {}", fileName);
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ DemoData data = new DemoData();
+ data.setString("String" + i);
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/PasswordProtectionExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/PasswordProtectionExample.java
new file mode 100644
index 000000000..572609433
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/PasswordProtectionExample.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.advanced;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.examples.write.data.DemoData;
+import org.apache.fesod.sheet.read.listener.ReadListener;
+
+/**
+ * Demonstrates reading and writing password-protected Excel files.
+ *
+ *
Scenario
+ *
You need to protect sensitive data (financial reports, personal information)
+ * with Excel's built-in password encryption. Fesod supports both writing encrypted
+ * files and reading them with the correct password.
+ *
+ *
Key Concepts
+ *
+ *
{@code .password("xxx")} — Available on both read and write builders.
+ * For write, it encrypts the output file. For read, it decrypts the input file.
+ *
Uses Excel's native encryption (AES for .xlsx), compatible with all Excel versions.
+ *
Without the correct password, reading will throw an
+ * {@code EncryptedDocumentException}.
The password must match the one used during write. If incorrect,
+ * an {@code EncryptedDocumentException} will be thrown.
+ *
+ * @param fileName input file path
+ * @param password decryption password
+ */
+ public static void passwordRead(String fileName, String password) {
+ FesodSheet.read(fileName, DemoData.class, new ReadListener() {
+ @Override
+ public void invoke(DemoData data, AnalysisContext context) {
+ log.info("Read password-protected data: {}", data);
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ log.info("Password-protected file read completed");
+ }
+ })
+ .password(password)
+ .sheet()
+ .doRead();
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ DemoData data = new DemoData();
+ data.setString("String" + i);
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/converter/CustomStringStringConverter.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/converter/CustomStringStringConverter.java
new file mode 100644
index 000000000..eeaded15f
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/converter/CustomStringStringConverter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.advanced.converter;
+
+import org.apache.fesod.sheet.converters.Converter;
+import org.apache.fesod.sheet.enums.CellDataTypeEnum;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.metadata.data.ReadCellData;
+import org.apache.fesod.sheet.metadata.data.WriteCellData;
+import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
+
+/**
+ * Custom String-to-String converter that adds a "Custom:" prefix during both read and write.
+ *
+ *
Unlike {@link org.apache.fesod.sheet.examples.read.converters.CustomStringStringConverter}
+ * (which uses the newer {@code ReadConverterContext}/{@code WriteConverterContext} API),
+ * this converter uses the legacy method signatures with
+ * {@link ExcelContentProperty} and {@link GlobalConfiguration} parameters.
+ * Both approaches are supported by Fesod.
+ *
+ * @see Converter
+ * @see org.apache.fesod.sheet.examples.advanced.CustomConverterExample
+ */
+public class CustomStringStringConverter 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 "Custom:" + cellData.getStringValue();
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(
+ String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+ return new WriteCellData<>("Custom:" + value);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/data/CustomConverterData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/data/CustomConverterData.java
new file mode 100644
index 000000000..3c7d7500b
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/advanced/data/CustomConverterData.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.advanced.data;
+
+import java.util.Date;
+import lombok.Data;
+import org.apache.fesod.sheet.annotation.ExcelProperty;
+import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
+import org.apache.fesod.sheet.annotation.format.NumberFormat;
+import org.apache.fesod.sheet.examples.advanced.converter.CustomStringStringConverter;
+
+/**
+ * Data model demonstrating three types of data conversion during Excel read/write.
+ *
+ *
This class uses a custom converter, a date format annotation, and a number format
+ * annotation to control how Excel cell values are converted to and from Java objects.
+ *
+ *
Conversion Mapping
+ *
+ * Field | Type | Annotation / Converter | Excel Cell → Java Value
+ * ───────────|────────|───────────────────────────────────────────|────────────────────────
+ * string | String | @ExcelProperty(converter=Custom...) | "Hello" → "Custom:Hello"
+ * date | Date | @DateTimeFormat("yyyy-MM-dd HH:mm:ss") | 2025-01-01 → Date object
+ * doubleData | Double | @NumberFormat("#.##%") | 0.56 → 0.56 (displayed as "56%")
+ *
+ *
+ *
Converter Types
+ *
+ *
Custom converter ({@link CustomStringStringConverter}) — Implements
+ * {@link org.apache.fesod.sheet.converters.Converter} for full control over the
+ * transformation logic. Applied via {@code @ExcelProperty(converter = ...)}.
+ *
Date format ({@code @DateTimeFormat}) — Specifies the date pattern used
+ * when converting between Excel date cells and {@link java.util.Date} objects.
+ *
Number format ({@code @NumberFormat}) — Specifies the number pattern used
+ * when converting between Excel numeric cells and {@link Double} values.
+ *
+ *
+ * @see CustomStringStringConverter
+ * @see DateTimeFormat
+ * @see NumberFormat
+ */
+@Data
+public class CustomConverterData {
+
+ /**
+ * Custom converter.
+ */
+ @ExcelProperty(converter = CustomStringStringConverter.class)
+ private String string;
+
+ /**
+ * Date format.
+ */
+ @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
+ private Date date;
+
+ /**
+ * Number format.
+ */
+ @NumberFormat("#.##%")
+ private Double doubleData;
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillBasicExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillBasicExample.java
new file mode 100644
index 000000000..8cb5f3d7b
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillBasicExample.java
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.fill;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.fill.data.FillData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+
+/**
+ * Demonstrates filling data into a pre-designed Excel template with placeholders.
+ *
+ *
Scenario
+ *
You have a pre-formatted Excel template (designed by a non-developer) with
+ * placeholder variables like {@code {name}} and {@code {number}}. You want to fill in
+ * actual data at runtime without rebuilding the layout programmatically.
+ *
+ *
Template Format
+ *
The template file ({@code templates/simple.xlsx}) contains cells with placeholders:
Object-based — Create a {@link FillData} object. Fesod maps field names
+ * to template placeholders automatically.
+ *
Map-based — Use a {@code Map} where keys match placeholder names.
+ * Useful when the structure is dynamic or you don't want to create a POJO.
+ *
+ *
+ *
Placeholder Syntax
+ *
+ *
{@code {fieldName}} — Replaced by a single value (for simple fills).
+ *
{@code {.fieldName}} — Replaced by a list of values (for list fills,
+ * see {@link FillComplexExample}).
+ *
+ *
+ *
When to Use Fill vs. Write
+ *
+ *
Fill — When you have a pre-designed template with specific formatting,
+ * merged cells, formulas, charts, or complex layouts.
+ *
Write — When generating a simple tabular report from scratch.
+ *
+ *
+ *
Related Examples
+ *
+ *
{@link FillComplexExample} — Fill templates with list data (multiple rows).
+ *
{@link org.apache.fesod.sheet.examples.write.BasicWriteExample} — Write from scratch.
+ *
+ *
+ * @see FesodSheet#write(String)
+ * @see FillData
+ */
+@Slf4j
+public class FillBasicExample {
+
+ public static void main(String[] args) {
+ simpleFill();
+ }
+
+ /**
+ * Fills a simple template using both object-based and map-based data sources.
+ *
+ *
Demonstrates two equivalent approaches:
+ *
+ *
Object fill — Uses a {@link FillData} POJO. The field names must match
+ * the placeholder names in the template.
+ *
Map fill — Uses a {@code Map}. The map keys must match
+ * the placeholder names. More flexible for dynamic structures.
+ *
+ * Both produce identical output files.
+ */
+ public static void simpleFill() {
+ String templateFileName = ExampleFileUtil.getExamplePath("templates" + File.separator + "simple.xlsx");
+
+ // Option 1: Fill based on an object
+ String fileName = ExampleFileUtil.getTempPath("simpleFill" + System.currentTimeMillis() + ".xlsx");
+ FillData fillData = new FillData();
+ fillData.setName("Zhang San");
+ fillData.setNumber(5.2);
+ FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
+ log.info("Successfully wrote file: {}", fileName);
+
+ // Option 2: Fill based on a Map
+ fileName = ExampleFileUtil.getTempPath("simpleFillMap" + System.currentTimeMillis() + ".xlsx");
+ Map map = new HashMap<>();
+ map.put("name", "Zhang San");
+ map.put("number", 5.2);
+ FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
+ log.info("Successfully wrote file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillComplexExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillComplexExample.java
new file mode 100644
index 000000000..af80ce0c4
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/FillComplexExample.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.fill;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.ExcelWriter;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.fill.data.FillData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.write.metadata.WriteSheet;
+
+/**
+ * Demonstrates filling list data into a template with repeating rows.
+ *
+ *
Scenario
+ *
Your template has a list area that should expand with multiple rows of data —
+ * for example, an invoice with line items, or a report with multiple data rows.
+ * The template uses {@code {.fieldName}} (dot prefix) placeholders to mark the
+ * repeating area.
+ *
+ *
Template Format
+ *
The template file ({@code templates/list.xlsx}) contains a single row with
+ * list placeholders:
+ *
+ * @see ExcelWriter#fill(Object, WriteSheet)
+ * @see FillData
+ */
+@Slf4j
+public class FillComplexExample {
+
+ public static void main(String[] args) {
+ listFill();
+ }
+
+ /**
+ * Fills a list template using both single-pass and multi-pass strategies.
+ *
+ *
Single-pass: All 10 rows loaded into memory, filled in one call.
+ * Multi-pass: Two batches of 10 rows each, filled via {@link ExcelWriter}
+ * with file-backed caching for lower memory usage.
+ */
+ public static void listFill() {
+ String templateFileName = ExampleFileUtil.getExamplePath("templates" + File.separator + "list.xlsx");
+
+ // Option 1: Load all data into memory at once and fill
+ String fileName = ExampleFileUtil.getTempPath("listFill" + System.currentTimeMillis() + ".xlsx");
+ FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(data());
+ log.info("Successfully wrote file: {}", fileName);
+
+ // Option 2: Fill in multiple passes, using file caching (saves memory)
+ fileName = ExampleFileUtil.getTempPath("listFillMultiple" + System.currentTimeMillis() + ".xlsx");
+ try (ExcelWriter excelWriter =
+ FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
+ WriteSheet writeSheet = FesodSheet.writerSheet().build();
+ excelWriter.fill(data(), writeSheet);
+ excelWriter.fill(data(), writeSheet);
+ }
+ log.info("Successfully wrote file: {}", fileName);
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ FillData fillData = new FillData();
+ fillData.setName("Zhang San" + i);
+ fillData.setNumber(5.2);
+ fillData.setDate(new Date());
+ list.add(fillData);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/issue1663/FillData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/data/FillData.java
similarity index 50%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/issue1663/FillData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/data/FillData.java
index f00fa5343..4045dd7a8 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/issue1663/FillData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/fill/data/FillData.java
@@ -17,16 +17,40 @@
* under the License.
*/
-package org.apache.fesod.sheet.temp.issue1663;
+package org.apache.fesod.sheet.examples.fill.data;
+import java.util.Date;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
+/**
+ * Data model for template fill operations.
+ *
+ *
Field names must exactly match the placeholder names in the Excel template.
+ * For example, if the template contains {@code {name}}, this class must have
+ * a field named {@code name} (or a getter {@code getName()}).
+ *
+ *
Template Placeholder Mapping
+ *
+ * Template Placeholder → Java Field
+ * ────────────────────────────────────
+ * {name} or {.name} → name (String)
+ * {number} or {.number} → number (double)
+ * {date} or {.date} → date (Date)
+ *
+ *
+ *
{@code @ExcelProperty} annotations are NOT needed for fill operations —
+ * the mapping is by field name to placeholder name.
+ *
+ * @see org.apache.fesod.sheet.examples.fill.FillBasicExample
+ * @see org.apache.fesod.sheet.examples.fill.FillComplexExample
+ */
@Getter
@Setter
@EqualsAndHashCode
public class FillData {
private String name;
private double number;
+ private Date date;
}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleReadExample.java
new file mode 100644
index 000000000..fb7ab16c7
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleReadExample.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.quickstart;
+
+import com.alibaba.fastjson2.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.quickstart.data.DemoData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.read.listener.PageReadListener;
+
+/**
+ * The simplest way to read an Excel file with Apache Fesod.
+ *
+ *
Scenario
+ *
You have an Excel file ({@code .xlsx} or {@code .xls}) and want to parse it into Java objects
+ * with minimal boilerplate. This is the recommended starting point for new users.
+ *
+ *
Key Concepts
+ *
+ *
{@link PageReadListener} — A built-in listener that collects rows into pages (default 100 rows per page)
+ * and delivers them via a lambda callback. Ideal for simple use cases.
+ *
{@link DemoData} — A POJO annotated with {@link org.apache.fesod.sheet.annotation.ExcelProperty}
+ * to map Excel columns to Java fields by header name.
+ *
Fesod reads row-by-row in a streaming fashion, so memory usage stays low even for large files.
+ *
+ *
+ *
Expected Behavior
+ *
When run against the bundled {@code demo.xlsx}, each row is logged as a JSON string:
+ *
{@code
+ * Read a row of data: {"string":"String0","date":"2025-01-01","doubleData":0.56}
+ * Read a row of data: {"string":"String1","date":"2025-01-02","doubleData":0.56}
+ * ...
+ * }
+ *
+ *
Related Examples
+ *
+ *
{@link org.apache.fesod.sheet.examples.read.BasicReadExample} — Uses a custom {@code ReadListener}
+ * with batch-save logic for production scenarios.
+ *
{@link org.apache.fesod.sheet.examples.quickstart.SimpleWriteExample} — The write counterpart.
Define a data model — Create a POJO with {@code @ExcelProperty} annotations
+ * (see {@link DemoData}).
+ *
Provide a listener — {@link PageReadListener} buffers rows and delivers them
+ * in batches via a lambda. You can also implement {@link org.apache.fesod.sheet.read.listener.ReadListener}
+ * directly for full control.
+ *
Call the builder — {@code FesodSheet.read(...).sheet().doRead()} starts
+ * the streaming parse, invoking your listener for each page of data.
+ *
+ *
+ *
Note: The file is read in a streaming fashion. Once {@code doRead()} returns,
+ * all rows have been processed and resources are automatically released.
+ */
+ public static void simpleRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file: {}", fileName);
+
+ // Specify the class to read the data, then read the first sheet.
+ FesodSheet.read(fileName, DemoData.class, new PageReadListener(dataList -> {
+ for (DemoData demoData : dataList) {
+ log.info("Read a row of data: {}", JSON.toJSONString(demoData));
+ }
+ }))
+ .sheet()
+ .doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleWriteExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleWriteExample.java
new file mode 100644
index 000000000..143d09cc6
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/SimpleWriteExample.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.quickstart;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.quickstart.data.DemoData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+
+/**
+ * The simplest way to write an Excel file with Apache Fesod.
+ *
+ *
Scenario
+ *
You have a list of Java objects and want to export them to an Excel file ({@code .xlsx}).
+ * This is the recommended starting point for new users who need to generate Excel reports.
+ *
+ *
Key Concepts
+ *
+ *
{@link FesodSheet#write(String, Class)} — Creates a write builder bound to a file path and data class.
+ *
{@link DemoData} — A POJO annotated with {@link org.apache.fesod.sheet.annotation.ExcelProperty}
+ * to define column headers. Fields annotated with {@link org.apache.fesod.sheet.annotation.ExcelIgnore}
+ * are excluded from the output.
+ *
{@code .sheet("Template")} — Names the worksheet tab in the output file.
+ *
{@code .doWrite(data)} — Terminal operation that writes all data and closes the file.
+ *
+ *
+ *
Expected Output
+ *
Generates an Excel file with headers derived from {@code @ExcelProperty} annotations:
+ *
+ * | String Title | Date Title | Number Title |
+ * |------------- |---------------------|--------------|
+ * | String0 | 2025-01-01 00:00:00 | 0.56 |
+ * | String1 | 2025-01-01 00:00:00 | 0.56 |
+ * | ... | ... | ... |
+ *
+ *
+ *
Related Examples
+ *
+ *
{@link org.apache.fesod.sheet.examples.write.StyleWriteExample} — Customize header and content styles.
+ *
{@link org.apache.fesod.sheet.examples.write.MergeWriteExample} — Merge cells during write.
+ *
{@link org.apache.fesod.sheet.examples.quickstart.SimpleReadExample} — The read counterpart.
+ *
+ *
+ * @see FesodSheet#write(String, Class)
+ * @see DemoData
+ */
+@Slf4j
+public class SimpleWriteExample {
+
+ public static void main(String[] args) {
+ simpleWrite();
+ }
+
+ /**
+ * Writes a list of {@link DemoData} objects to an Excel file in two simple steps.
+ *
+ *
+ *
Define a data model — Create a POJO with {@code @ExcelProperty} annotations
+ * (see {@link DemoData}). The annotation value becomes the column header.
+ *
Call the builder — {@code FesodSheet.write(fileName, DemoData.class).sheet("name").doWrite(list)}
+ * generates the file. The builder handles file creation, header writing, and resource cleanup.
+ *
+ *
+ *
Output location: The file is written to the system temp directory.
+ * Check the log output for the exact path.
+ */
+ public static void simpleWrite() {
+ // Write to system temp directory for output files
+ String fileName = ExampleFileUtil.getTempPath("demo" + System.currentTimeMillis() + ".xlsx");
+
+ // Specify the class to write, then write to the first sheet named "Template"
+ FesodSheet.write(fileName, DemoData.class).sheet("Template").doWrite(data());
+ log.info("Successfully wrote file: {}", fileName);
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ DemoData data = new DemoData();
+ data.setString("String" + i);
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/data/DemoData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/data/DemoData.java
new file mode 100644
index 000000000..a2b394274
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/quickstart/data/DemoData.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.quickstart.data;
+
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fesod.sheet.annotation.ExcelIgnore;
+import org.apache.fesod.sheet.annotation.ExcelProperty;
+
+/**
+ * Data model for the quickstart examples, mapping Java fields to Excel columns.
+ *
+ *
Column Mapping
+ *
Each field annotated with {@link ExcelProperty} maps to an Excel column by header name.
+ * The generated Excel file will have these columns:
+ *
+ * | String Title | Date Title | Number Title |
+ * |------------- |---------------------|--------------|
+ * | (string) | (date) | (doubleData) |
+ *
+ *
+ *
Annotations Used
+ *
+ *
{@link ExcelProperty} — Maps a field to an Excel column by header name or index.
+ *
{@link ExcelIgnore} — Excludes a field from both reading and writing.
+ * Useful for internal/transient fields like IDs or computed values.
+ *
+ *
+ *
Supported Field Types
+ *
Fesod automatically converts between Excel cell types and common Java types:
+ * {@code String}, {@code Date}, {@code Double}, {@code Integer}, {@code BigDecimal}, etc.
+ * For custom conversions, see
+ * {@link org.apache.fesod.sheet.examples.read.data.ConverterData}.
+ *
+ * @see ExcelProperty
+ * @see ExcelIgnore
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class DemoData {
+ /**
+ * String Title
+ */
+ @ExcelProperty("String Title")
+ private String string;
+
+ /**
+ * Date Title
+ */
+ @ExcelProperty("Date Title")
+ private Date date;
+
+ /**
+ * Number Title
+ */
+ @ExcelProperty("Number Title")
+ private Double doubleData;
+
+ /**
+ * Ignore this field
+ */
+ @ExcelIgnore
+ private String ignore;
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/BasicReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/BasicReadExample.java
new file mode 100644
index 000000000..940488473
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/BasicReadExample.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.data.DemoData;
+import org.apache.fesod.sheet.examples.read.listeners.DemoDataListener;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+
+/**
+ * Demonstrates the standard pattern for reading Excel files with a custom {@link DemoDataListener}.
+ *
+ *
Scenario
+ *
You need to read an Excel file and process rows in batches (e.g., inserting into a database
+ * every 100 rows). This is the production-recommended pattern for reading Excel files with Fesod.
+ *
+ *
Key Concepts
+ *
+ *
{@link DemoDataListener} — A custom {@link org.apache.fesod.sheet.read.listener.ReadListener}
+ * that implements batch caching and database persistence. See the listener class for
+ * the full lifecycle details.
+ *
{@link DemoData} — POJO model class mapping Excel columns via {@code @ExcelProperty}.
+ *
Unlike {@link org.apache.fesod.sheet.examples.quickstart.SimpleReadExample} which uses
+ * the convenience {@code PageReadListener}, this approach gives full control over
+ * the row processing lifecycle.
+ *
+ *
+ *
Listener Lifecycle
+ *
+ * File opened
+ * │
+ * ├─ invoke(data, context) ← called for each data row
+ * │ └─ batch save every 100 rows
+ * │
+ * └─ doAfterAllAnalysed(context) ← called once after last row
+ * └─ final batch save
+ *
+ *
+ *
Expected Behavior
+ *
Each row is logged as JSON. Every 100 rows (or at end of file), a batch save is triggered.
{@link ConverterReadExample} — Read with data format conversion.
+ *
{@link MultiSheetReadExample} — Read multiple sheets from one file.
+ *
+ *
+ * @see DemoDataListener
+ * @see org.apache.fesod.sheet.read.listener.ReadListener
+ */
+@Slf4j
+public class BasicReadExample {
+
+ public static void main(String[] args) {
+ basicRead();
+ }
+
+ /**
+ * Reads an Excel file using a custom {@link DemoDataListener}.
+ *
+ *
The listener handles row-by-row processing with batch persistence.
+ * A new listener instance is created per read operation to avoid shared state issues.
+ *
+ *
Important: Never reuse a listener instance across multiple read operations
+ * or make it a Spring singleton — it holds mutable state (the cached data list).
+ */
+ public static void basicRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file: {}", fileName);
+
+ // Specify the class to read the data, then read the first sheet.
+ FesodSheet.read(fileName, DemoData.class, new DemoDataListener())
+ .sheet()
+ .doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ConverterReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ConverterReadExample.java
new file mode 100644
index 000000000..6406ffae1
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ConverterReadExample.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import com.alibaba.fastjson2.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.data.ConverterData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.read.listener.PageReadListener;
+
+/**
+ * Demonstrates reading Excel files with data format converters.
+ *
+ *
Scenario
+ *
Your Excel file contains dates and numbers in specific formats (e.g., "2025-01-01 12:30:00"
+ * or "56.00%"), and you want them read as formatted {@code String} values rather than raw types.
+ * Or you need a completely custom transformation for a column.
+ *
+ *
Key Concepts
+ *
+ *
{@link org.apache.fesod.sheet.annotation.format.DateTimeFormat} — Converts date cells
+ * to formatted strings using the specified pattern (e.g., {@code "yyyy-MM-dd HH:mm:ss"}).
+ *
{@link org.apache.fesod.sheet.annotation.format.NumberFormat} — Converts number cells
+ * to formatted strings using {@link java.text.DecimalFormat} patterns (e.g., {@code "#.##%"}).
+ *
{@link org.apache.fesod.sheet.examples.read.converters.CustomStringStringConverter} —
+ * A custom converter that prepends "Custom:" to string values, demonstrating
+ * the {@link org.apache.fesod.sheet.converters.Converter} interface.
All fields in {@link ConverterData} are {@code String} type. Fesod applies the configured
+ * converter/format to transform the raw Excel cell value before setting the field.
+ *
+ *
Related Examples
+ *
+ *
{@link org.apache.fesod.sheet.examples.advanced.CustomConverterExample} —
+ * Register a custom converter at the builder level (applies to all matching fields).
+ *
{@link BasicReadExample} — Read without converters (automatic type mapping).
+ *
+ *
+ * @see ConverterData
+ * @see org.apache.fesod.sheet.converters.Converter
+ * @see org.apache.fesod.sheet.annotation.format.DateTimeFormat
+ * @see org.apache.fesod.sheet.annotation.format.NumberFormat
+ */
+@Slf4j
+public class ConverterReadExample {
+
+ public static void main(String[] args) {
+ converterRead();
+ }
+
+ /**
+ * Reads an Excel file with converters and format annotations applied.
+ *
+ *
Uses {@link PageReadListener} for simplicity. The actual conversion happens
+ * transparently during parsing — by the time your listener receives the data,
+ * all fields are already converted according to their annotations.
+ */
+ public static void converterRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file with converters: {}", fileName);
+
+ FesodSheet.read(fileName, ConverterData.class, new PageReadListener(dataList -> {
+ for (ConverterData data : dataList) {
+ log.info("Read a row of data with converter: {}", JSON.toJSONString(data));
+ }
+ }))
+ .sheet()
+ .doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ExceptionHandlingExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ExceptionHandlingExample.java
new file mode 100644
index 000000000..84843a9ec
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/ExceptionHandlingExample.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.data.ExceptionDemoData;
+import org.apache.fesod.sheet.examples.read.listeners.ExceptionListener;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+
+/**
+ * Demonstrates graceful error handling when reading Excel files with data conversion issues.
+ *
+ *
Scenario
+ *
Your Excel file contains messy or inconsistent data — for example, a column expected to be
+ * a date contains plain text like "N/A". Instead of failing the entire read, you want to:
+ *
+ *
Log the problematic row and column
+ *
Skip the bad row
+ *
Continue processing the remaining rows
+ *
+ *
+ *
Key Concepts
+ *
+ *
{@link org.apache.fesod.sheet.read.listener.ReadListener#onException(Exception, org.apache.fesod.sheet.context.AnalysisContext)}
+ * — Override this method to intercept conversion errors. If you don't rethrow,
+ * parsing continues to the next row.
+ *
{@link org.apache.fesod.sheet.exception.ExcelDataConvertException} — Thrown when a cell value
+ * cannot be converted to the target Java type. Provides {@code getRowIndex()},
+ * {@code getColumnIndex()}, and {@code getCellData()} for precise error reporting.
+ *
{@link ExceptionDemoData} — Intentionally uses {@code Date} type for a string column
+ * to trigger conversion errors.
Rows with valid dates are processed normally. Rows with incompatible data are logged
+ * with their exact row/column position and skipped.
+ *
+ *
Related Examples
+ *
+ *
{@link BasicReadExample} — Read without error handling (default: exceptions propagate).
+ *
+ *
+ * @see org.apache.fesod.sheet.exception.ExcelDataConvertException
+ * @see ExceptionListener
+ */
+@Slf4j
+public class ExceptionHandlingExample {
+
+ public static void main(String[] args) {
+ exceptionRead();
+ }
+
+ /**
+ * Reads an Excel file with an {@link ExceptionListener} that catches and logs conversion errors.
+ *
+ *
The {@link ExceptionDemoData} model intentionally maps a string column to {@code Date},
+ * causing {@link org.apache.fesod.sheet.exception.ExcelDataConvertException} to be thrown.
+ * The listener catches these errors, logs them, and lets parsing continue.
+ */
+ public static void exceptionRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file with exception handling: {}", fileName);
+
+ FesodSheet.read(fileName, ExceptionDemoData.class, new ExceptionListener())
+ .sheet()
+ .doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/IndexOrNameReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/IndexOrNameReadExample.java
new file mode 100644
index 000000000..19c3ec128
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/IndexOrNameReadExample.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import com.alibaba.fastjson2.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.data.IndexOrNameData;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.read.listener.PageReadListener;
+
+/**
+ * Demonstrates reading Excel columns by positional index or header name.
+ *
+ *
Scenario
+ *
Your Excel file may have columns in an unpredictable order, or you only need a subset
+ * of columns. Instead of relying on field declaration order, you can explicitly map fields
+ * to columns by index (0-based position) or by header name.
+ *
+ *
Key Concepts
+ *
+ *
{@code @ExcelProperty(index = 2)} — Forces the field to read from the 3rd column (0-based),
+ * regardless of header name or field order.
+ *
{@code @ExcelProperty("String")} — Matches the column whose header text is "String".
+ * This is resilient to column reordering.
+ *
Priority: {@code index} > {@code order} > default field declaration order.
+ * If both {@code index} and name are specified, {@code index} takes priority.
Excel columns may be shuffled by users (use name matching).
+ *
You need only specific columns from a wide spreadsheet (use index).
+ *
Different Excel versions have different column orders (use name matching).
+ *
+ *
+ *
Related Examples
+ *
+ *
{@link BasicReadExample} — Default column matching by field order.
+ *
{@link NoModelReadExample} — Read without any model (raw map data).
+ *
+ *
+ * @see IndexOrNameData
+ * @see org.apache.fesod.sheet.annotation.ExcelProperty
+ */
+@Slf4j
+public class IndexOrNameReadExample {
+
+ public static void main(String[] args) {
+ indexOrNameRead();
+ }
+
+ /**
+ * Reads using {@link IndexOrNameData} which combines index-based and name-based column matching.
+ *
+ *
The {@code doubleData} field reads from column index 2, while {@code string} and
+ * {@code date} fields match by header name. This flexible approach handles varying
+ * column layouts gracefully.
+ */
+ public static void indexOrNameRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file with index/name mapping: {}", fileName);
+
+ FesodSheet.read(fileName, IndexOrNameData.class, new PageReadListener(dataList -> {
+ for (IndexOrNameData data : dataList) {
+ log.info("Read a row of data with index or name: {}", JSON.toJSONString(data));
+ }
+ }))
+ .sheet()
+ .doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/MultiSheetReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/MultiSheetReadExample.java
new file mode 100644
index 000000000..4d11f0de9
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/MultiSheetReadExample.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.ExcelReader;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.data.DemoData;
+import org.apache.fesod.sheet.examples.read.listeners.DemoDataListener;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.read.metadata.ReadSheet;
+
+/**
+ * Demonstrates reading multiple sheets from a single Excel workbook.
+ *
+ *
Scenario
+ *
Your Excel workbook contains multiple sheets (tabs), each with its own data.
+ * You need to read some or all of them, potentially with different data models
+ * or listeners for each sheet.
+ *
+ *
Two Approaches
+ *
+ *
Approach 1: Read All Sheets ({@code doReadAll()})
+ *
Reads every sheet in the workbook using the same data model and listener.
+ * Simplest approach when all sheets share the same structure.
+ *
{@code
+ * FesodSheet.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
+ * }
Creates an {@link ExcelReader} and configures individual {@link ReadSheet} objects.
+ * Each sheet can have its own data model, listener, and configuration.
+ * The {@code ExcelReader} must be closed after use (use try-with-resources).
Each sheet's rows are delivered to its respective listener in order.
+ * When using {@code doReadAll()}, all sheets share the same listener instance,
+ * so the listener receives rows from all sheets sequentially.
+ *
+ *
Related Examples
+ *
+ *
{@link BasicReadExample} — Single-sheet read.
+ *
{@link NoModelReadExample} — Read without a data model.
Approach 1 uses {@code doReadAll()} for simplicity.
+ * Approach 2 uses {@code ExcelReader} with individual {@code ReadSheet} configurations
+ * for full control over each sheet's data model and listener.
+ *
+ *
Note: In Approach 2, the {@link ExcelReader} is wrapped in try-with-resources
+ * to ensure proper resource cleanup. Always close the reader after use.
+ */
+ public static void repeatedRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading multiple sheets from file: {}", fileName);
+
+ // 1. Read all sheets
+ FesodSheet.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
+ log.info("Read all sheets completed");
+
+ // 2. Read specific sheets
+ try (ExcelReader excelReader = FesodSheet.read(fileName).build()) {
+ // Create ReadSheet objects for each sheet you want to read.
+ ReadSheet readSheet1 = FesodSheet.readSheet(0)
+ .head(DemoData.class)
+ .registerReadListener(new DemoDataListener())
+ .build();
+ ReadSheet readSheet2 = FesodSheet.readSheet(1)
+ .head(DemoData.class)
+ .registerReadListener(new DemoDataListener())
+ .build();
+
+ // Read multiple sheets at once.
+ excelReader.read(readSheet1, readSheet2);
+ }
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/NoModelReadExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/NoModelReadExample.java
new file mode 100644
index 000000000..4fe9c311f
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/NoModelReadExample.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.read.listeners.NoModelDataListener;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+
+/**
+ * Demonstrates reading an Excel file without defining a Java data model.
+ *
+ *
Scenario
+ *
You don't know the Excel file's structure at compile time, or you want a quick way
+ * to inspect any Excel file without creating a POJO. Each row is returned as a
+ * {@code Map} where the key is the column index (0-based)
+ * and the value is the cell content as a string.
+ *
+ *
Key Concepts
+ *
+ *
Omit the data class parameter from {@code FesodSheet.read()} — Fesod will use
+ * {@code Map} as the default row type.
Building generic Excel import tools that handle arbitrary file structures.
+ *
Quick prototyping or debugging — inspect what's in a file.
+ *
Schema-free processing where columns are dynamic.
+ *
+ *
+ *
Related Examples
+ *
+ *
{@link BasicReadExample} — Type-safe read with a data model (recommended for production).
+ *
{@link IndexOrNameReadExample} — Flexible column matching with index or name.
+ *
+ *
+ * @see NoModelDataListener
+ * @see org.apache.fesod.sheet.event.AnalysisEventListener
+ */
+@Slf4j
+public class NoModelReadExample {
+
+ public static void main(String[] args) {
+ noModelRead();
+ }
+
+ /**
+ * Reads an Excel file without specifying a data class.
+ *
+ *
When no class is provided to {@code FesodSheet.read()}, each row arrives
+ * as a {@code Map}. The {@link NoModelDataListener} processes
+ * these maps with the same batch-save pattern used in typed listeners.
+ */
+ public static void noModelRead() {
+ String fileName = ExampleFileUtil.getExamplePath("demo.xlsx");
+ log.info("Reading file (no model): {}", fileName);
+
+ // No need to specify a class, just use NoModelDataListener.
+ FesodSheet.read(fileName, new NoModelDataListener()).sheet().doRead();
+
+ log.info("Successfully read file: {}", fileName);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CustomStringStringConverter.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/converters/CustomStringStringConverter.java
similarity index 53%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CustomStringStringConverter.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/converters/CustomStringStringConverter.java
index f553a38bc..8e6551b6b 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CustomStringStringConverter.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/converters/CustomStringStringConverter.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.read;
+package org.apache.fesod.sheet.examples.read.converters;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.converters.ReadConverterContext;
@@ -26,9 +26,36 @@
import org.apache.fesod.sheet.metadata.data.WriteCellData;
/**
- * String and string converter
+ * Custom converter that transforms string cell values by prepending a prefix.
*
+ *
Scenario
+ *
You need custom transformation logic that goes beyond simple formatting —
+ * for example, adding prefixes, decrypting values, or looking up reference data.
+ * Implement the {@link Converter} interface to create a reusable converter.
Per-field: {@code @ExcelProperty(converter = CustomStringStringConverter.class)}
+ * on a specific field (see {@link org.apache.fesod.sheet.examples.read.data.ConverterData}).
+ *
Global: {@code .registerConverter(new CustomStringStringConverter())} on the builder
+ * (see {@link org.apache.fesod.sheet.examples.advanced.CustomConverterExample}).
+ * Applies to all fields matching the Java type + Excel type key.
+ *
+ *
+ *
Type Keys
+ *
{@link #supportJavaTypeKey()} returns {@code String.class} and
+ * {@link #supportExcelTypeKey()} returns {@code CellDataTypeEnum.STRING},
+ * meaning this converter handles String↔String conversions only.
+ *
+ * @see Converter
+ * @see org.apache.fesod.sheet.annotation.ExcelProperty#converter()
*/
public class CustomStringStringConverter implements Converter {
@@ -42,24 +69,11 @@ public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
- /**
- * This method is called when reading data from Excel.
- *
- * @param context The context containing the cell data read from Excel.
- * @return The converted Java data.
- */
@Override
public String convertToJavaData(ReadConverterContext> context) {
return "Custom:" + context.getReadCellData().getStringValue();
}
- /**
- * This method is called when writing data to Excel.
- * (This is not relevant in this context and can be ignored.)
- *
- * @param context The context containing the Java data to be written.
- * @return The converted Excel data.
- */
@Override
public WriteCellData> convertToExcelData(WriteConverterContext context) {
return new WriteCellData<>(context.getValue());
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ConverterData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ConverterData.java
new file mode 100644
index 000000000..c5c0e718d
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ConverterData.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read.data;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fesod.sheet.annotation.ExcelProperty;
+import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
+import org.apache.fesod.sheet.annotation.format.NumberFormat;
+import org.apache.fesod.sheet.examples.read.converters.CustomStringStringConverter;
+
+/**
+ * Data model demonstrating three types of data conversion during Excel reading.
+ *
+ *
All fields in this class are {@code String} type, but Fesod applies converters
+ * and format annotations to transform the raw Excel cell values before setting them.
Custom converter ({@link CustomStringStringConverter}) — Implements
+ * {@link org.apache.fesod.sheet.converters.Converter} for full control over the
+ * transformation logic. Applied via {@code @ExcelProperty(converter = ...)}.
+ *
Date format ({@code @DateTimeFormat}) — Uses {@link java.text.SimpleDateFormat}
+ * patterns to format date cells as strings.
+ *
Number format ({@code @NumberFormat}) — Uses {@link java.text.DecimalFormat}
+ * patterns to format numeric cells as strings.
+ *
+ *
+ * @see CustomStringStringConverter
+ * @see org.apache.fesod.sheet.annotation.format.DateTimeFormat
+ * @see org.apache.fesod.sheet.annotation.format.NumberFormat
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class ConverterData {
+
+ /**
+ * Custom converter.
+ */
+ @ExcelProperty(converter = CustomStringStringConverter.class)
+ private String string;
+
+ /**
+ * Date format.
+ */
+ @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
+ private String date;
+
+ /**
+ * Number format.
+ */
+ @NumberFormat("#.##%")
+ private String doubleData;
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoDAO.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoDAO.java
new file mode 100644
index 000000000..50112e54b
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoDAO.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read.data;
+
+import java.util.List;
+
+/**
+ * Mock Data Access Object (DAO) simulating database persistence for examples.
+ *
+ *
In production, replace the {@code save()} method body with actual database operations
+ * (e.g., JDBC batch insert, MyBatis, or JPA). The batch pattern used in
+ * {@link org.apache.fesod.sheet.examples.read.listeners.DemoDataListener} calls this
+ * DAO every 100 rows to balance memory usage and database round-trips.
+ *
+ * @see org.apache.fesod.sheet.examples.read.listeners.DemoDataListener
+ */
+public class DemoDAO {
+
+ public void save(List list) {
+ // In actual use, you can use batch insert here.
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/csv/CsvData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoData.java
similarity index 51%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/csv/CsvData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoData.java
index f7c8319d7..c1d124c49 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/temp/csv/CsvData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/DemoData.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.temp.csv;
+package org.apache.fesod.sheet.examples.read.data;
import java.util.Date;
import lombok.EqualsAndHashCode;
@@ -27,24 +27,49 @@
import org.apache.fesod.sheet.annotation.ExcelProperty;
/**
- * TODO
+ * Data model for the read examples, mapping Excel columns to Java fields.
*
+ *
This class demonstrates the standard pattern for Fesod data models:
+ * annotate fields with {@link ExcelProperty} to map them to Excel columns by header name.
+ * Use {@link ExcelIgnore} to exclude fields that should not participate in reading or writing.
*
+ *
Column Mapping
+ *
+ * Excel Column: | String Title | Date Title | Number Title |
+ * Java Field: | string | date | doubleData |
+ * Java Type: | String | Date | Double |
+ *
+ *
+ *
The {@code ignore} field is excluded from Excel operations via {@code @ExcelIgnore},
+ * making it suitable for internal-only data like database IDs or computed values.
+ *
+ * @see ExcelProperty
+ * @see ExcelIgnore
*/
@Getter
@Setter
@EqualsAndHashCode
-public class CsvData {
- @ExcelProperty("字符串标题")
+public class DemoData {
+ /**
+ * String Title
+ */
+ @ExcelProperty("String Title")
private String string;
- @ExcelProperty("日期标题")
+ /**
+ * Date Title
+ */
+ @ExcelProperty("Date Title")
private Date date;
- @ExcelProperty("数字标题")
+ /**
+ * Number Title
+ */
+ @ExcelProperty("Number Title")
private Double doubleData;
+
/**
- * 忽略这个字段
+ * Ignore this field
*/
@ExcelIgnore
private String ignore;
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ExceptionDemoData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ExceptionDemoData.java
similarity index 55%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ExceptionDemoData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ExceptionDemoData.java
index 77e0e0f70..e2a72a29a 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ExceptionDemoData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/ExceptionDemoData.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.read;
+package org.apache.fesod.sheet.examples.read.data;
import java.util.Date;
import lombok.EqualsAndHashCode;
@@ -25,17 +25,25 @@
import lombok.Setter;
/**
- * Basic data class. The order here is consistent with the order in the Excel file.
+ * Data model intentionally designed to trigger conversion errors during reading.
*
+ *
This class maps all Excel data to a single {@code Date} field. When the Excel file
+ * contains string values (e.g., "String Title") that cannot be parsed as dates,
+ * Fesod throws an {@link org.apache.fesod.sheet.exception.ExcelDataConvertException}.
*
- **/
+ *
Used by {@link org.apache.fesod.sheet.examples.read.ExceptionHandlingExample} to
+ * demonstrate the {@code onException()} callback in
+ * {@link org.apache.fesod.sheet.examples.read.listeners.ExceptionListener}.
+ *
+ * @see org.apache.fesod.sheet.examples.read.ExceptionHandlingExample
+ * @see org.apache.fesod.sheet.exception.ExcelDataConvertException
+ */
@Getter
@Setter
@EqualsAndHashCode
public class ExceptionDemoData {
-
/**
- * Using a Date to receive a string will definitely cause an error.
+ * Using a Date to receive a string will cause an error.
*/
private Date date;
}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/IndexOrNameData.java
similarity index 50%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/IndexOrNameData.java
index 9290584e7..8c61767ee 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/data/IndexOrNameData.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.read;
+package org.apache.fesod.sheet.examples.read.data;
import java.util.Date;
import lombok.EqualsAndHashCode;
@@ -26,22 +26,38 @@
import org.apache.fesod.sheet.annotation.ExcelProperty;
/**
- * Basic data class
+ * Data model demonstrating mixed index-based and name-based column matching.
*
+ *
When both {@code index} and name are specified on the same field, {@code index} wins.
+ * The full priority order is: {@code index} > {@code order} > field declaration order.
+ *
+ *
Tip: Use index-based matching when the Excel column position is fixed and known.
+ * Use name-based matching when users might reorder columns but headers remain consistent.
+ *
+ * @see org.apache.fesod.sheet.annotation.ExcelProperty
+ * @see org.apache.fesod.sheet.examples.read.IndexOrNameReadExample
+ */
@Getter
@Setter
@EqualsAndHashCode
public class IndexOrNameData {
/**
- * Force reading the third column. It is not recommended to use both index and name at the same time.
- * Either use index only or use name only for matching within a single object.
+ * Force reading the third column.
*/
@ExcelProperty(index = 2)
private Double doubleData;
/**
- * Match by name. Note that if the name is duplicated, only one field will be populated with data.
+ * Match by name.
*/
@ExcelProperty("String")
private String string;
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/DemoDataListener.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/DemoDataListener.java
new file mode 100644
index 000000000..e35d86ee2
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/DemoDataListener.java
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.read.listeners;
+
+import com.alibaba.fastjson2.JSON;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.common.util.ListUtils;
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.examples.read.data.DemoDAO;
+import org.apache.fesod.sheet.examples.read.data.DemoData;
+import org.apache.fesod.sheet.read.listener.ReadListener;
+
+/**
+ * Production-pattern listener demonstrating batch read-and-persist for Excel data.
+ *
+ *
Scenario
+ *
Reading a large Excel file (thousands or millions of rows) and inserting the data
+ * into a database. Loading all rows into memory at once would cause an OutOfMemoryError.
+ * This listener accumulates rows in a small batch and persists every {@value #BATCH_COUNT}
+ * rows, then clears the cache.
+ *
+ *
Lifecycle
+ *
+ * ┌───────────────────────────────────────────────────────────────────┐
+ * │ invoke(data, context) ← called once per data row │
+ * │ └─ add to cachedDataList │
+ * │ └─ if cache.size() >= 100 → saveData() → clear cache │
+ * ├───────────────────────────────────────────────────────────────────┤
+ * │ doAfterAllAnalysed(context) ← called once after last row │
+ * │ └─ saveData() for remaining rows in cache │
+ * └───────────────────────────────────────────────────────────────────┘
+ *
+ *
+ *
Thread Safety & Lifecycle
+ *
IMPORTANT: This class must NOT be managed by Spring (or any IoC container) as a
+ * singleton. Create a new instance for each read operation because:
+ *
+ *
The {@code cachedDataList} is mutable state that must not be shared.
+ *
Reusing a listener across files would mix data from different files.
+ *
+ *
+ *
Spring Integration
+ *
If you need to inject Spring beans (e.g., a real DAO), use the constructor that
+ * accepts a {@link DemoDAO} parameter. Create the listener in your service method:
+ *
{@code
+ * @Service
+ * public class ExcelService {
+ * @Autowired
+ * private DemoDAO demoDAO;
+ *
+ * public void importExcel(String fileName) {
+ * // Create a NEW listener for each read, passing the Spring-managed DAO
+ * FesodSheet.read(fileName, DemoData.class, new DemoDataListener(demoDAO))
+ * .sheet().doRead();
+ * }
+ * }
+ * }
+ *
+ * @see ReadListener
+ * @see org.apache.fesod.sheet.examples.read.BasicReadExample
+ */
+@Slf4j
+public class DemoDataListener implements ReadListener {
+
+ /**
+ * Store data in the database every 100 records.
+ */
+ private static final int BATCH_COUNT = 100;
+ /**
+ * Cached data
+ */
+ private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ /**
+ * Mock DAO
+ */
+ private DemoDAO demoDAO;
+
+ public DemoDataListener() {
+ // This is a demo, so a new instance is created here.
+ demoDAO = new DemoDAO();
+ }
+
+ /**
+ * If Spring is used, please use this constructor.
+ *
+ * @param demoDAO
+ */
+ public DemoDataListener(DemoDAO demoDAO) {
+ this.demoDAO = demoDAO;
+ }
+
+ /**
+ * This method will be called for each data parsed.
+ *
+ * @param data one row value.
+ * @param context
+ */
+ @Override
+ public void invoke(DemoData data, AnalysisContext context) {
+ log.info("Parsed one row of data: {}", JSON.toJSONString(data));
+ cachedDataList.add(data);
+ if (cachedDataList.size() >= BATCH_COUNT) {
+ saveData();
+ cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ }
+ }
+
+ /**
+ * This method will be called after all data has been parsed.
+ *
+ * @param context
+ */
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ saveData();
+ log.info("All data has been parsed!");
+ }
+
+ /**
+ * Save data to database.
+ */
+ private void saveData() {
+ log.info("{} records saved to database!", cachedDataList.size());
+ demoDAO.save(cachedDataList);
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExceptionListener.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/ExceptionListener.java
similarity index 58%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExceptionListener.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/ExceptionListener.java
index f6b0c0b30..d3e57fdb7 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExceptionListener.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/ExceptionListener.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.read;
+package org.apache.fesod.sheet.examples.read.listeners;
import com.alibaba.fastjson2.JSON;
import java.util.List;
@@ -25,37 +25,53 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.common.util.ListUtils;
import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.examples.read.data.ExceptionDemoData;
import org.apache.fesod.sheet.exception.ExcelDataConvertException;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.read.listener.ReadListener;
/**
- * Read and convert exceptions.
+ * Listener that demonstrates graceful exception handling during Excel reading.
*
+ *
Scenario
+ *
When reading an Excel file with inconsistent data (e.g., text in a date column),
+ * Fesod throws an {@link ExcelDataConvertException}. This listener catches those errors,
+ * logs diagnostic information, and allows parsing to continue for the remaining rows.
*
+ *
Key Methods
+ *
+ *
{@link #onException(Exception, AnalysisContext)} — Intercepts any exception during parsing.
+ * If the exception is an {@code ExcelDataConvertException}, logs the exact row, column,
+ * and cell value that caused the error. Not rethrowing the exception tells Fesod
+ * to skip this row and continue.
+ *
{@link #invokeHead(Map, AnalysisContext)} — Called when the header row is parsed.
+ * Receives a map of column index → {@link ReadCellData} containing header cell information.
+ *
{@link #invoke(ExceptionDemoData, AnalysisContext)} — Called for each successfully
+ * converted data row.
+ *
{@link #doAfterAllAnalysed(AnalysisContext)} — Called after the last row.
+ * Persists any remaining cached data.
+ *
+ *
+ *
Error Handling Strategy
+ *
+ * onException() is called:
+ * ├─ ExcelDataConvertException → log row/column/value, continue
+ * └─ Other exception → log message, continue (or rethrow to stop)
+ *
+ *
+ * @see ExcelDataConvertException
+ * @see org.apache.fesod.sheet.examples.read.ExceptionHandlingExample
*/
@Slf4j
-public class DemoExceptionListener implements ReadListener {
+public class ExceptionListener implements ReadListener {
- /**
- * Save to the database every 5 records. In actual use, it can be set to 100 records, and then clear the list to facilitate memory recycling.
- */
- private static final int BATCH_COUNT = 5;
+ private static final int BATCH_COUNT = 100;
private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- /**
- * This interface will be called in case of conversion exceptions or other exceptions. If an exception is thrown here, reading will be stopped. If no exception is thrown, the next line will continue to be read.
- *
- * @param exception The exception that occurred.
- * @param context The analysis context.
- * @throws Exception If an exception needs to be propagated.
- */
@Override
public void onException(Exception exception, AnalysisContext context) {
log.error("Parsing failed, but continue parsing the next line: {}", exception.getMessage());
- // If it is a cell conversion exception, the specific row number can be obtained.
- // If the header information is needed, use it in conjunction with invokeHeadMap.
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
log.error(
@@ -66,12 +82,6 @@ public void onException(Exception exception, AnalysisContext context) {
}
}
- /**
- * This method will return the header row line by line.
- *
- * @param headMap The header map containing column index and cell data.
- * @param context The analysis context.
- */
@Override
public void invokeHead(Map> headMap, AnalysisContext context) {
log.info("Parsed a header row: {}", JSON.toJSONString(headMap));
@@ -80,6 +90,7 @@ public void invokeHead(Map> headMap, AnalysisContext co
@Override
public void invoke(ExceptionDemoData data, AnalysisContext context) {
log.info("Parsed a data row: {}", JSON.toJSONString(data));
+ cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@@ -92,11 +103,7 @@ public void doAfterAllAnalysed(AnalysisContext context) {
log.info("All data parsing completed!");
}
- /**
- * Save data to the database.
- */
private void saveData() {
- log.info("{} records, starting to save to the database!", cachedDataList.size());
- log.info("Data saved to the database successfully!");
+ log.info("{} records saved to database!", cachedDataList.size());
}
}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/NoModelDataListener.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/NoModelDataListener.java
similarity index 62%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/NoModelDataListener.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/NoModelDataListener.java
index 16c805f8f..046b5ec05 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/NoModelDataListener.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/read/listeners/NoModelDataListener.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.read;
+package org.apache.fesod.sheet.examples.read.listeners;
import com.alibaba.fastjson2.JSON;
import java.util.List;
@@ -28,17 +28,41 @@
import org.apache.fesod.sheet.event.AnalysisEventListener;
/**
- * Directly receive data using a map.
+ * Listener for schema-free Excel reading without a data model.
*
+ *
Scenario
+ *
When you don't have (or don't want) a POJO mapped to the Excel structure,
+ * this listener receives each row as a {@code Map} where:
+ *
+ *
Key = column index (0-based)
+ *
Value = cell content as a string
+ *
*
+ *
Key Differences from Typed Listeners
+ *
+ *
Extends {@link AnalysisEventListener}{@code
+ *
No {@code @ExcelProperty} annotations needed — all columns are automatically included.
+ *
All values arrive as strings, even if the Excel cell is numeric or date type.
+ *
+ *
+ *
When to Use
+ *
+ *
Generic Excel import tools that accept any file layout.
+ *
Quick data inspection / debugging.
+ *
Dynamic column handling where structure is unknown at compile time.
+ *
+ *
+ * @see org.apache.fesod.sheet.examples.read.NoModelReadExample
+ * @see AnalysisEventListener
*/
@Slf4j
public class NoModelDataListener extends AnalysisEventListener
+ */
+ public static void mergeWrite() {
+ String fileName = ExampleFileUtil.getTempPath("mergeWrite" + System.currentTimeMillis() + ".xlsx");
+
+ // Method 1: Use annotations (see DemoMergeData)
+ FesodSheet.write(fileName, DemoMergeData.class)
+ .sheet("Annotation Merge")
+ .doWrite(data());
+ log.info("Successfully wrote file: {}", fileName);
+
+ // Method 2: Use a merge strategy
+ fileName = ExampleFileUtil.getTempPath("mergeWriteStrategy" + System.currentTimeMillis() + ".xlsx");
+ // Merge every 2 rows in the 0th column.
+ LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
+ FesodSheet.write(fileName, DemoMergeData.class)
+ .registerWriteHandler(loopMergeStrategy)
+ .sheet("Strategy Merge")
+ .doWrite(data());
+ log.info("Successfully wrote file: {}", fileName);
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ DemoMergeData data = new DemoMergeData();
+ data.setString("String" + (i / 2)); // Same string for merged rows
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/StyleWriteExample.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/StyleWriteExample.java
new file mode 100644
index 000000000..6b36c2703
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/StyleWriteExample.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.write;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fesod.sheet.FesodSheet;
+import org.apache.fesod.sheet.examples.util.ExampleFileUtil;
+import org.apache.fesod.sheet.examples.write.data.DemoStyleData;
+
+/**
+ * Demonstrates how to customize header and content styles in Excel output.
+ *
+ *
Scenario
+ *
You're generating an Excel report that needs branded colors, custom font sizes,
+ * or specific cell formatting to match corporate style guides.
+ *
+ *
Key Concepts
+ *
+ *
Class-level annotations — Apply default styles to all columns:
+ *
+ *
{@code @HeadStyle} / {@code @HeadFontStyle} — Header row background color and font.
+ *
{@code @ContentStyle} / {@code @ContentFontStyle} — Data row background color and font.
+ *
+ *
+ *
Field-level annotations — Override class-level styles for specific columns.
+ * In the example, the "String Title" column has distinct colors and a larger font
+ * than the other columns.
+ *
Color values — Use Excel's indexed color system (0-63). Common values:
+ * 10 = dark green, 14 = dark teal, 17 = light yellow, 40 = light blue.
+ * See {@link org.apache.poi.ss.usermodel.IndexedColors} for the full palette.
Styles are defined entirely through annotations on {@link DemoStyleData}.
+ * No programmatic style code is needed — Fesod reads the annotations and applies
+ * them automatically during write.
+ */
+ public static void styleWrite() {
+ String fileName = ExampleFileUtil.getTempPath("styleWrite" + System.currentTimeMillis() + ".xlsx");
+
+ FesodSheet.write(fileName, DemoStyleData.class).sheet("Template").doWrite(data());
+ log.info("Successfully wrote file: {}", fileName);
+ }
+
+ private static List data() {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ DemoStyleData data = new DemoStyleData();
+ data.setString("String" + i);
+ data.setDate(new Date());
+ data.setDoubleData(0.56);
+ list.add(data);
+ }
+ return list;
+ }
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoData.java
similarity index 67%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoData.java
index bc0c31868..c191ab781 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoData.java
@@ -17,23 +17,30 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.data;
import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
+import lombok.Data;
import org.apache.fesod.sheet.annotation.ExcelIgnore;
import org.apache.fesod.sheet.annotation.ExcelProperty;
/**
- * Basic data class
+ * Data model for the write examples, mapping Java fields to Excel columns.
*
+ *
Identical structure to the read examples' DemoData, but uses Lombok's
+ * {@code @Data} for brevity (generates getters, setters, equals, hashCode, toString).
+ * The {@link ExcelProperty} annotations define column headers in the output file,
+ * while {@link ExcelIgnore} excludes the {@code ignore} field from the Excel output.
*
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
+ *
Generated Excel Headers
+ *
+ * | String Title | Date Title | Number Title |
+ *
+ *
+ * @see ExcelProperty
+ * @see ExcelIgnore
+ */
+@Data
public class DemoData {
/**
* String Title
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoMergeData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoMergeData.java
similarity index 57%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoMergeData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoMergeData.java
index 0e33c2071..afc8d2efc 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoMergeData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoMergeData.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.data;
import java.util.Date;
import lombok.EqualsAndHashCode;
@@ -27,17 +27,33 @@
import org.apache.fesod.sheet.annotation.write.style.ContentLoopMerge;
/**
- * Style data class
+ * Data model demonstrating annotation-based cell merging.
*
+ *
The {@code @ContentLoopMerge(eachRow = 2)} annotation on the {@code string} field
+ * tells Fesod to merge every 2 consecutive rows in that column. This is useful for
+ * category grouping in reports.
For runtime-configurable merging, use {@link org.apache.fesod.sheet.write.merge.LoopMergeStrategy}
+ * instead (see {@link org.apache.fesod.sheet.examples.write.MergeWriteExample}).
+ *
+ * @see ContentLoopMerge
+ * @see org.apache.fesod.sheet.write.merge.LoopMergeStrategy
+ */
@Getter
@Setter
@EqualsAndHashCode
-// Merge columns 2-3 of rows 6-7 into one cell
-// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)
public class DemoMergeData {
- // Merge this column every 2 rows
+ /**
+ * Merge cells every 2 rows in this column.
+ */
@ContentLoopMerge(eachRow = 2)
@ExcelProperty("String Title")
private String string;
@@ -45,6 +61,6 @@ public class DemoMergeData {
@ExcelProperty("Date Title")
private Date date;
- @ExcelProperty("Double Title")
+ @ExcelProperty("Number Title")
private Double doubleData;
}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoStyleData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoStyleData.java
similarity index 61%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoStyleData.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoStyleData.java
index 3b21486f3..1a4685648 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/DemoStyleData.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/DemoStyleData.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.data;
import java.util.Date;
import lombok.EqualsAndHashCode;
@@ -31,29 +31,47 @@
import org.apache.fesod.sheet.enums.poi.FillPatternTypeEnum;
/**
- * Style data class
+ * Data model demonstrating annotation-based style customization.
*
+ *
Class-Level Styles (Default for All Columns)
+ *
+ *
Header: Solid fill, color 10 (dark green), font size 20pt
+ *
Content: Solid fill, color 17 (light yellow), font size 20pt
Header: Solid fill, color 14 (dark teal), font size 30pt
+ *
Content: Solid fill, color 40 (light blue), font size 30pt
+ *
+ *
+ *
Style Annotations Reference
+ *
+ *
{@link HeadStyle} / {@link ContentStyle} — Cell background fill pattern and color.
+ * Use {@code fillPatternType = SOLID_FOREGROUND} with {@code fillForegroundColor} for
+ * solid background colors.
+ *
{@link HeadFontStyle} / {@link ContentFontStyle} — Font properties like size,
+ * bold, italic, color, and font name.
+ *
Field-level annotations override class-level annotations for that specific column.
+ *
+ *
+ * @see HeadStyle
+ * @see HeadFontStyle
+ * @see ContentStyle
+ * @see ContentFontStyle
+ * @see org.apache.fesod.sheet.examples.write.StyleWriteExample
+ */
@Getter
@Setter
@EqualsAndHashCode
-// Head background red
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
-// Head font size 20
@HeadFontStyle(fontHeightInPoints = 20)
-// Content background green
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
-// Content font size 20
@ContentFontStyle(fontHeightInPoints = 20)
public class DemoStyleData {
- // String head background pink
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)
- // String head font size 30
@HeadFontStyle(fontHeightInPoints = 30)
- // String content background sky blue
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)
- // String content font size 30
@ContentFontStyle(fontHeightInPoints = 30)
@ExcelProperty("String Title")
private String string;
@@ -61,6 +79,6 @@ public class DemoStyleData {
@ExcelProperty("Date Title")
private Date date;
- @ExcelProperty("Double Title")
+ @ExcelProperty("Number Title")
private Double doubleData;
}
diff --git a/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/ImageDemoData.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/ImageDemoData.java
new file mode 100644
index 000000000..562131247
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/data/ImageDemoData.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package org.apache.fesod.sheet.examples.write.data;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fesod.sheet.annotation.ExcelProperty;
+import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
+import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
+import org.apache.fesod.sheet.converters.string.StringImageConverter;
+import org.apache.fesod.sheet.metadata.data.WriteCellData;
+
+/**
+ * Data model demonstrating five ways to export images to Excel cells.
+ *
+ *
Image Source Types
+ *
Each field represents a different way to provide image data to Fesod:
+ *
+ * Field | Type | Description
+ * ───────────────────|───────────────────────|────────────────────────────────────────
+ * file | File | Local file reference
+ * inputStream | InputStream | Stream from any source
+ * string | String | File path (requires StringImageConverter)
+ * byteArray | byte[] | Raw image bytes
+ * url | URL | Remote image URL
+ * writeCellDataFile | WriteCellData<Void> | Advanced: multiple images + text in one cell
+ *
+ *
+ *
Layout Annotations
+ *
+ *
{@code @ContentRowHeight(100)} — Sets row height to 100 points for image visibility.
+ *
{@code @ColumnWidth(100 / 8)} — Sets column width (in characters, ~12.5) for image cells.
+ *
+ *
+ *
Memory Warning
+ *
All images are loaded into memory. For large volumes, consider:
+ *
+ *
Uploading to cloud storage and using URL references.
+ *
Compressing images before export.
+ *
+ *
+ * @see org.apache.fesod.sheet.examples.write.ImageWriteExample
+ * @see StringImageConverter
+ * @see WriteCellData
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+@ContentRowHeight(100)
+@ColumnWidth(100 / 8)
+public class ImageDemoData {
+ private File file;
+ private InputStream inputStream;
+ /**
+ * If string type, a converter must be specified.
+ */
+ @ExcelProperty(converter = StringImageConverter.class)
+ private String string;
+
+ private byte[] byteArray;
+ /**
+ * Export by URL.
+ */
+ private URL url;
+
+ /**
+ * Export by file and set the export position.
+ */
+ private WriteCellData writeCellDataFile;
+}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CommentWriteHandler.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CommentWriteHandler.java
similarity index 55%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CommentWriteHandler.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CommentWriteHandler.java
index af2063c31..75668c030 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CommentWriteHandler.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CommentWriteHandler.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.handlers;
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.common.util.BooleanUtils;
@@ -30,9 +30,37 @@
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
/**
- * Custom handler. Add comment to the header of the first row.
+ * Custom {@link RowWriteHandler} that adds an Excel comment (note) to a header cell.
*
+ *
Scenario
+ *
You want to add hover-able comments/notes to specific cells — for example, adding
+ * instructions or descriptions to column headers so end-users understand each column.
*
+ *
How It Works
+ *
+ *
Implements {@link RowWriteHandler#afterRowDispose(RowWriteHandlerContext)},
+ * which is called after each row is written.
+ *
Checks if the row is a header row via {@code context.getHead()}.
+ *
Creates a drawing patriarch on the sheet and adds a comment anchored
+ * to cell B1 (row 0, column 1).
+ *
+ *
+ *
Result
+ *
The second column header cell will show a small red triangle indicator.
+ * Hovering over it displays "Created a comment!".
Note: This handler uses Apache POI's XSSF-specific classes ({@code XSSFClientAnchor},
+ * {@code XSSFRichTextString}), so it only works with {@code .xlsx} format.
+ *
+ * @see RowWriteHandler
+ * @see org.apache.poi.ss.usermodel.Comment
*/
@Slf4j
public class CommentWriteHandler implements RowWriteHandler {
@@ -42,12 +70,10 @@ public void afterRowDispose(RowWriteHandlerContext context) {
if (BooleanUtils.isTrue(context.getHead())) {
Sheet sheet = context.getWriteSheetHolder().getSheet();
Drawing> drawingPatriarch = sheet.createDrawingPatriarch();
- // Create a comment in the first row, second column
+ // Create a comment in the first row, second column.
Comment comment =
drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 1, 0, (short) 2, 1));
- // Input comment information
- comment.setString(new XSSFRichTextString("Create comment!"));
- // Add comment to cell object
+ comment.setString(new XSSFRichTextString("Created a comment!"));
sheet.getRow(0).getCell(1).setCellComment(comment);
}
}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomCellWriteHandler.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomCellWriteHandler.java
similarity index 53%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomCellWriteHandler.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomCellWriteHandler.java
index 01dfc4a33..0d26f53ae 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomCellWriteHandler.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomCellWriteHandler.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.handlers;
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.common.util.BooleanUtils;
@@ -29,9 +29,40 @@
import org.apache.poi.ss.usermodel.Hyperlink;
/**
- * Custom handler
+ * Custom {@link CellWriteHandler} that adds a hyperlink to the first header cell.
*
+ *
Scenario
+ *
You want to customize individual cells after they are written — for example,
+ * adding hyperlinks, conditional formatting, or cell validation. The {@code CellWriteHandler}
+ * gives you access to the Apache POI {@link Cell} object for low-level customization.
*
+ *
How It Works
+ *
+ *
Implements {@link CellWriteHandler#afterCellDispose(CellWriteHandlerContext)},
+ * which is called after each cell (both header and content cells).
+ *
Checks if the cell is a header cell ({@code context.getHead()}) in the first
+ * column ({@code cell.getColumnIndex() == 0}).
+ *
Creates a URL hyperlink pointing to the Fesod GitHub repository and attaches
+ * it to the cell.
+ *
+ *
+ *
Result
+ *
The first column header cell becomes a clickable hyperlink to
+ * {@code https://github.com/apache/fesod}.
Fesod calls write handlers in registration order. If multiple handlers modify
+ * the same cell, later handlers can override earlier ones.
+ *
+ * @see CellWriteHandler
+ * @see org.apache.poi.ss.usermodel.Hyperlink
*/
@Slf4j
public class CustomCellWriteHandler implements CellWriteHandler {
@@ -39,13 +70,12 @@ public class CustomCellWriteHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
- // Can perform any operation on the cell here
- log.info("Finished writing row {}, column {}", cell.getRowIndex(), cell.getColumnIndex());
+ log.info("Row {}, Column {} write completed.", cell.getRowIndex(), cell.getColumnIndex());
if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 0) {
CreationHelper createHelper =
context.getWriteSheetHolder().getSheet().getWorkbook().getCreationHelper();
Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
- hyperlink.setAddress("https://github.com/fast-excel/fastexcel");
+ hyperlink.setAddress("https://github.com/apache/fesod");
cell.setHyperlink(hyperlink);
}
}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomSheetWriteHandler.java b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomSheetWriteHandler.java
similarity index 53%
rename from fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomSheetWriteHandler.java
rename to fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomSheetWriteHandler.java
index 02c28ec25..6f14e3cbf 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomSheetWriteHandler.java
+++ b/fesod-examples/fesod-sheet-examples/src/main/java/org/apache/fesod/sheet/examples/write/handlers/CustomSheetWriteHandler.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fesod.sheet.demo.write;
+package org.apache.fesod.sheet.examples.write.handlers;
import lombok.extern.slf4j.Slf4j;
import org.apache.fesod.sheet.write.handler.SheetWriteHandler;
@@ -28,19 +28,52 @@
import org.apache.poi.ss.util.CellRangeAddressList;
/**
- * Custom handler. Add dropdown to first column of first and second data rows, displaying "Test1", "Test2"
+ * Custom {@link SheetWriteHandler} that adds a dropdown validation list to a sheet.
*
+ *
Scenario
+ *
You want to add data validation, conditional formatting, or other sheet-level
+ * customizations when a worksheet is first created. The {@code SheetWriteHandler}
+ * provides a hook into the sheet creation lifecycle.
*
+ *
How It Works
+ *
+ *
Implements {@link SheetWriteHandler#afterSheetCreate(SheetWriteHandlerContext)},
+ * called immediately after a new sheet is created.
+ *
Creates a data validation constraint with explicit list values
+ * ({@code "Test1", "Test2"}).
+ *
Applies the constraint to cells A2:A3 (rows 1-2, column 0).
+ *
+ *
+ *
Result
+ *
Cells A2 and A3 will show a dropdown arrow. Clicking it reveals the options
+ * "Test1" and "Test2". Entering other values triggers a validation error.
+ *
+ * @see SheetWriteHandler
+ * @see org.apache.poi.ss.usermodel.DataValidation
*/
@Slf4j
public class CustomSheetWriteHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(SheetWriteHandlerContext context) {
- log.info("Sheet {} write success.", context.getWriteSheetHolder().getSheetNo());
+ log.info("Sheet {} write successful.", context.getWriteSheetHolder().getSheetNo());
- // Set range for first column, first and second data rows. Since the first row is head, the data is actually in
- // the 2nd and 3rd rows.
+ // Add a dropdown list to the first column of the second and third rows.
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);
DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"Test1", "Test2"});
diff --git a/fesod-examples/fesod-sheet-examples/src/main/resources/application.properties b/fesod-examples/fesod-sheet-examples/src/main/resources/application.properties
new file mode 100644
index 000000000..9afc34590
--- /dev/null
+++ b/fesod-examples/fesod-sheet-examples/src/main/resources/application.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+server.port=8080
diff --git a/fesod-examples/fesod-sheet-examples/src/main/resources/example/demo.xlsx b/fesod-examples/fesod-sheet-examples/src/main/resources/example/demo.xlsx
new file mode 100644
index 000000000..89e43a71d
Binary files /dev/null and b/fesod-examples/fesod-sheet-examples/src/main/resources/example/demo.xlsx differ
diff --git a/fesod-examples/fesod-sheet-examples/src/test/resources/converter/img.jpg b/fesod-examples/fesod-sheet-examples/src/main/resources/example/sample-data/img.jpg
similarity index 100%
rename from fesod-examples/fesod-sheet-examples/src/test/resources/converter/img.jpg
rename to fesod-examples/fesod-sheet-examples/src/main/resources/example/sample-data/img.jpg
diff --git a/fesod-examples/fesod-sheet-examples/src/test/resources/demo/fill/list.xlsx b/fesod-examples/fesod-sheet-examples/src/main/resources/example/templates/list.xlsx
similarity index 100%
rename from fesod-examples/fesod-sheet-examples/src/test/resources/demo/fill/list.xlsx
rename to fesod-examples/fesod-sheet-examples/src/main/resources/example/templates/list.xlsx
diff --git a/fesod-examples/fesod-sheet-examples/src/test/resources/demo/fill/simple.xlsx b/fesod-examples/fesod-sheet-examples/src/main/resources/example/templates/simple.xlsx
similarity index 100%
rename from fesod-examples/fesod-sheet-examples/src/test/resources/demo/fill/simple.xlsx
rename to fesod-examples/fesod-sheet-examples/src/main/resources/example/templates/simple.xlsx
diff --git a/fesod-examples/fesod-sheet-examples/src/test/resources/logback.xml b/fesod-examples/fesod-sheet-examples/src/main/resources/logback.xml
similarity index 84%
rename from fesod-examples/fesod-sheet-examples/src/test/resources/logback.xml
rename to fesod-examples/fesod-sheet-examples/src/main/resources/logback.xml
index 6ef9578b8..b18f5687b 100644
--- a/fesod-examples/fesod-sheet-examples/src/test/resources/logback.xml
+++ b/fesod-examples/fesod-sheet-examples/src/main/resources/logback.xml
@@ -20,16 +20,17 @@
-->
-
-
- ${CONSOLE_LOG_PATTERN}
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%nutf8
-
+
+
+
+
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/fill/FillTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/fill/FillTest.java
deleted file mode 100644
index 1d65a91cf..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/fill/FillTest.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.fill;
-
-import java.io.File;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.common.util.MapUtils;
-import org.apache.fesod.sheet.ExcelWriter;
-import org.apache.fesod.sheet.FesodSheet;
-import org.apache.fesod.sheet.enums.WriteDirectionEnum;
-import org.apache.fesod.sheet.util.TestFileUtil;
-import org.apache.fesod.sheet.write.handler.SheetWriteHandler;
-import org.apache.fesod.sheet.write.handler.context.SheetWriteHandlerContext;
-import org.apache.fesod.sheet.write.metadata.WriteSheet;
-import org.apache.fesod.sheet.write.metadata.fill.FillConfig;
-import org.apache.fesod.sheet.write.metadata.fill.FillWrapper;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.util.CellRangeAddress;
-import org.junit.jupiter.api.Test;
-
-/**
- * Example of writing and filling data into Excel
- */
-public class FillTest {
- /**
- * Simplest example of filling data
- */
- @Test
- public void simpleFill() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx";
-
- // Option 1: Fill based on an object
- String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
- // This will fill the first sheet, and the file stream will be automatically closed.
- FillData fillData = new FillData();
- fillData.setName("Zhang San");
- fillData.setNumber(5.2);
- FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
-
- // Option 2: Fill based on a Map
- fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
- // This will fill the first sheet, and the file stream will be automatically closed.
- Map map = MapUtils.newHashMap();
- map.put("name", "Zhang San");
- map.put("number", 5.2);
- FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
- }
-
- /**
- * Example of filling a list
- */
- @Test
- public void listFill() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- // When filling a list, note that {.} in the template indicates a list.
- // If the object filling the list is a Map, it must contain all keys of the list, even if the data is null. Use
- // map.put(key, null).
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx";
-
- // Option 1: Load all data into memory at once and fill
- String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
- // This will fill the first sheet, and the file stream will be automatically closed.
- FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(data());
-
- // Option 2: Fill in multiple passes, using file caching (saves memory)
- fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet().build();
- excelWriter.fill(data(), writeSheet);
- excelWriter.fill(data(), writeSheet);
- }
- }
-
- /**
- * Example of complex filling
- */
- @Test
- public void complexFill() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- // {} represents a normal variable, {.} represents a list variable.
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx";
-
- String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx";
- // Option 1
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet().build();
- // Note: The forceNewRow parameter is used here. When writing a list, it will always create a new row, and
- // the data below will be shifted down. Default is false, which will use the next row if available,
- // otherwise create a new one.
- // forceNewRow: If set to true, it will load all data into memory, so use it with caution.
- // In short, if your template has a list and the list is not the last row, and there is data below that
- // needs to be filled, you must set forceNewRow=true. However, this will consume a lot of memory.
- // For large datasets where the list is not the last row, refer to the next example.
- FillConfig fillConfig =
- FillConfig.builder().forceNewRow(Boolean.TRUE).build();
- excelWriter.fill(data(), fillConfig, writeSheet);
- excelWriter.fill(data(), fillConfig, writeSheet);
- Map map = MapUtils.newHashMap();
- map.put("date", "2019-10-09 13:28:28");
- map.put("total", 1000);
- excelWriter.fill(map, writeSheet);
- }
- }
-
- /**
- * Example of complex filling with large datasets
- *
- * The solution here is to ensure that the list in the template is the last row, and then append a table. For Excel 2003, there is no solution other than increasing memory.
- */
- @Test
- public void complexFillWithTable() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- // {} represents a normal variable, {.} represents a list variable.
- // Here, the template deletes the data after the list, i.e., the summary row.
- String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator
- + "complexFillWithTable.xlsx";
-
- String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx";
-
- // Option 1
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet().build();
- // Directly write data
- excelWriter.fill(data(), writeSheet);
- excelWriter.fill(data(), writeSheet);
-
- // Write data before the list
- Map map = new HashMap();
- map.put("date", "2019-10-09 13:28:28");
- excelWriter.fill(map, writeSheet);
-
- // There is a summary after the list, which needs to be written manually.
- // Here, we use a list for simplicity. You can also use an object.
- List> totalListList = ListUtils.newArrayList();
- List totalList = ListUtils.newArrayList();
- totalListList.add(totalList);
- totalList.add(null);
- totalList.add(null);
- totalList.add(null);
- // Fourth column
- totalList.add("Total:1000");
- // Note: Use write here, not fill.
- excelWriter.write(totalListList, writeSheet);
- // Overall, the writing is complex, but there is no better solution. Asynchronous writing to Excel does not
- // support row deletion or movement, nor does it support writing comments, so this approach is used.
- // The idea is to create a new sheet and copy data bit by bit. However, when adding rows to the list, the
- // data in the columns below cannot be shifted. A better solution will be explored in the future.
- }
- }
-
- /**
- * Example of horizontal filling
- */
- @Test
- public void horizontalFill() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- // {} represents a normal variable, {.} represents a list variable.
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx";
-
- String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx";
- // Option 1
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet().build();
- FillConfig fillConfig = FillConfig.builder()
- .direction(WriteDirectionEnum.HORIZONTAL)
- .build();
- excelWriter.fill(data(), fillConfig, writeSheet);
- excelWriter.fill(data(), fillConfig, writeSheet);
-
- Map map = new HashMap<>();
- map.put("date", "2019-10-09 13:28:28");
- excelWriter.fill(map, writeSheet);
- }
- }
-
- /**
- * Example of composite filling with multiple lists
- */
- @Test
- public void compositeFill() {
- // Template note: Use {} to indicate variables. If there are existing "{", "}" characters, use "\{", "\}"
- // instead.
- // {} represents a normal variable, {.} represents a list variable, {prefix.} prefix can distinguish different
- // lists.
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx";
-
- String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx";
-
- // Option 1
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName).withTemplate(templateFileName).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet().build();
- FillConfig fillConfig = FillConfig.builder()
- .direction(WriteDirectionEnum.HORIZONTAL)
- .build();
- // If there are multiple lists, the template must have {prefix.}. Here, the prefix is data1, and multiple
- // lists must be wrapped with FillWrapper.
- excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet);
- excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet);
- excelWriter.fill(new FillWrapper("data2", data()), writeSheet);
- excelWriter.fill(new FillWrapper("data2", data()), writeSheet);
- excelWriter.fill(new FillWrapper("data3", data()), writeSheet);
- excelWriter.fill(new FillWrapper("data3", data()), writeSheet);
-
- Map map = new HashMap();
- map.put("date", new Date());
-
- excelWriter.fill(map, writeSheet);
- }
- }
-
- /**
- * Example of filling an Excel template with date formatting.
- *
- * This method demonstrates how to fill an Excel template where date fields
- * are already formatted in the template. The written data will automatically
- * follow the predefined date format in the template.
- */
- @Test
- public void dateFormatFill() {
- // Define the path to the template file.
- // The template should have predefined date formatting.
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "dateFormat.xlsx";
-
- // Generate a new output file name with a timestamp to avoid overwriting.
- String fileName = TestFileUtil.getPath() + "dateFormatFill" + System.currentTimeMillis() + ".xlsx";
-
- // Fill the template with data.
- // The dates in the data will be formatted according to the template's settings.
- FesodSheet.write(fileName).withTemplate(templateFileName).sheet().doFill(data());
- }
-
- @Test
- public void testFillSheetDispose() {
- String templateFileName =
- TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx";
- String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
- FillData fillData = new FillData();
- fillData.setName("Zhang San");
- fillData.setNumber(5.2);
- FesodSheet.write(fileName)
- .withTemplate(templateFileName)
- .sheet()
- .registerWriteHandler(new SheetWriteHandler() {
- @Override
- public void afterSheetDispose(SheetWriteHandlerContext context) {
- Sheet sheet = context.getWriteSheetHolder().getSheet();
- sheet.addMergedRegionUnsafe(new CellRangeAddress(1, 2, 0, 1));
- }
- })
- .doFill(fillData);
- }
-
- private List data() {
- List list = ListUtils.newArrayList();
- for (int i = 0; i < 10; i++) {
- FillData fillData = new FillData();
- list.add(fillData);
- fillData.setName("Zhang San");
- fillData.setNumber(5.2);
- fillData.setDate(new Date());
- }
- return list;
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/rare/WriteTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/rare/WriteTest.java
deleted file mode 100644
index f47697e91..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/rare/WriteTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.rare;
-
-import java.io.File;
-import java.util.Date;
-import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.ExcelWriter;
-import org.apache.fesod.sheet.FesodSheet;
-import org.apache.fesod.sheet.demo.read.DemoData;
-import org.apache.fesod.sheet.util.FileUtils;
-import org.apache.fesod.sheet.util.TestFileUtil;
-import org.apache.fesod.sheet.write.handler.RowWriteHandler;
-import org.apache.fesod.sheet.write.handler.WorkbookWriteHandler;
-import org.apache.fesod.sheet.write.handler.context.RowWriteHandlerContext;
-import org.apache.fesod.sheet.write.handler.context.WorkbookWriteHandlerContext;
-import org.apache.fesod.sheet.write.metadata.WriteSheet;
-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.xssf.streaming.SXSSFWorkbook;
-import org.junit.jupiter.api.Test;
-
-/**
- * Record some uncommon cases
- *
- *
- */
-@Slf4j
-public class WriteTest {
-
- /**
- * Compress temporary files
- * When exporting an Excel file in xlsx format, a temporary XML file will be generated, which can be quite large.
- * If disk space is limited, you can compress these files.
- * Note that compression consumes performance.
- */
- @Test
- public void compressedTemporaryFile() {
- log.info("Temporary XML files are stored at: {}", FileUtils.getPoiFilesPath());
- File file = TestFileUtil.createNewFile("rare/compressedTemporaryFile" + System.currentTimeMillis() + ".xlsx");
-
- // Specify which class to use for writing here
- try (ExcelWriter excelWriter = FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new WorkbookWriteHandler() {
-
- /**
- * Intercept the Workbook creation completion event
- * @param context
- */
- @Override
- public void afterWorkbookCreate(WorkbookWriteHandlerContext context) {
- // Get the Workbook object
- Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
- // Temporary files are only generated in SXSSFWorkbook mode
- if (workbook instanceof SXSSFWorkbook) {
- SXSSFWorkbook sxssfWorkbook = (SXSSFWorkbook) workbook;
- // Enable temporary file compression. Note that this will consume CPU performance, but the
- // temporary files will be smaller
- sxssfWorkbook.setCompressTempFiles(true);
- }
- }
- })
- .build()) {
- // Note that the same sheet should only be created once
- WriteSheet writeSheet = FesodSheet.writerSheet("Template").build();
- // 100,000 data entries to ensure sufficient space
- for (int i = 0; i < 10000; i++) {
- // Query data from the database page by page. Here you can query data for each page from the database
- List data = data();
- excelWriter.write(data, writeSheet);
- }
- log.info("Writing completed, preparing to migrate and compress files.");
- }
- }
-
- /**
- * Write data to a specified cell
- */
- @Test
- public void specifiedCellWrite() {
- File file = TestFileUtil.createNewFile("rare/specifiedCellWrite" + System.currentTimeMillis() + ".xlsx");
-
- // It is necessary to distinguish whether it is before or after the last row
- // The reason for the distinction is: Excel can only move forward, and only 100 rows are stored in memory. The
- // afterRowDispose event is called after each row is written, so modifying a row requires intercepting this
- // event
- // If it is after the last row, since there will be no more data afterwards, just intercept the
- // afterWorkbookDispose event and write the data when the Excel file is almost done
- FesodSheet.write(file, DemoData.class)
- // Writing value before the last row
- .registerWriteHandler(new RowWriteHandler() {
- @Override
- public void afterRowDispose(RowWriteHandlerContext context) {
- if (context.getRow().getRowNum() == 2) {
- Cell cell = context.getRow().getCell(2);
- if (cell == null) {
- cell = context.getRow().createCell(2);
- }
- cell.setCellValue("Test data for the second row");
- }
- }
- })
- // Writing value after the last row
- .registerWriteHandler(new WorkbookWriteHandler() {
- @Override
- public void afterWorkbookDispose(WorkbookWriteHandlerContext context) {
- Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(99);
- if (row == null) {
- row = sheet.createRow(99);
- }
- Cell cell = row.getCell(2);
- if (cell == null) {
- cell = row.createCell(2);
- }
- cell.setCellValue("Test data for row 99");
- }
- })
- .sheet("Template")
- .doWrite(data());
-
- log.info("Writing to file completed:{}", file);
- }
-
- private List data() {
- List list = ListUtils.newArrayList();
- for (int i = 0; i < 10; i++) {
- DemoData data = new DemoData();
- data.setString("String" + i);
- data.setDate(new Date());
- data.setDoubleData(0.56);
- list.add(data);
- }
- return list;
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataDemoHeadDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataDemoHeadDataListener.java
deleted file mode 100644
index f648a25a4..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataDemoHeadDataListener.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-
-/**
- * Read header
- *
- *
- */
-@Slf4j
-public class CellDataDemoHeadDataListener implements ReadListener {
- /**
- * Store data to database every 5 records. In actual use, it can be 100 records, then clear the list to facilitate memory recycling
- */
- private static final int BATCH_COUNT = 100;
-
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- @Override
- public void invoke(CellDataReadDemoData data, AnalysisContext context) {
- log.info("Parsed one record: {}", JSON.toJSONString(data));
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- log.info("All data parsing completed!");
- }
-
- /**
- * Save data to database
- */
- private void saveData() {
- log.info("{} records, starting to save to database!", cachedDataList.size());
- log.info("Successfully saved to database!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataReadDemoData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataReadDemoData.java
deleted file mode 100644
index 432d069a2..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/CellDataReadDemoData.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.metadata.data.CellData;
-
-/**
- * Basic data class. The order here is consistent with the order in Excel
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class CellDataReadDemoData {
- private CellData string;
- // Note that although this is a date, the type stored is number because excel stores it as number
- private CellData date;
- private CellData doubleData;
- // This may not be perfectly retrieved. Some formulas are dependent and may not be read. This issue will be fixed
- // later
- private CellData formulaValue;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterData.java
deleted file mode 100644
index e8d67e74d..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterData.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
-import org.apache.fesod.sheet.annotation.format.NumberFormat;
-
-/**
- * Basic data class. The order here is consistent with the order in the Excel file.
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class ConverterData {
-
- /**
- * I use a custom converter. No matter what is passed from the database, I prepend "Custom:".
- */
- @ExcelProperty(converter = CustomStringStringConverter.class)
- private String string;
-
- /**
- * I use a string to receive the date so that it can be formatted. I want to receive the date in the format of yyyy-MM-dd HH:mm:ss.
- */
- @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
- private String date;
-
- /**
- * I want to receive a number in percentage format.
- */
- @NumberFormat("#.##%")
- private String doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterDataListener.java
deleted file mode 100644
index 127574947..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ConverterDataListener.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-
-/**
- * Template data reading class
- *
- *
- */
-@Slf4j
-public class ConverterDataListener implements ReadListener {
-
- /**
- * Save to the database every 5 records. In actual use, you might use 100 records,
- * then clear the list to facilitate memory recycling.
- */
- private static final int BATCH_COUNT = 5;
-
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- @Override
- public void invoke(ConverterData data, AnalysisContext context) {
- log.info("Parsed a piece of data: {}", JSON.toJSONString(data));
- cachedDataList.add(data);
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- log.info("All data has been parsed and processed!");
- }
-
- /**
- * Simulate saving data to the database
- */
- private void saveData() {
- log.info("Saving {} records to the database!", cachedDataList.size());
- log.info("Data saved to the database successfully!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoChainAccessorsData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoChainAccessorsData.java
deleted file mode 100644
index 24e45ed20..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoChainAccessorsData.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import java.util.Date;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * 基础数据类.这里的排序和excel里面的排序一致
- *
- *
- **/
-@Data
-@Accessors(chain = true)
-public class DemoChainAccessorsData {
- private String string;
- private Date date;
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoCompatibleHeaderDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoCompatibleHeaderDataListener.java
deleted file mode 100644
index 064d4b3b4..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoCompatibleHeaderDataListener.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.event.AnalysisEventListener;
-import org.apache.fesod.sheet.metadata.data.ReadCellData;
-
-/**
- * Listener to read headers with compatibility for both Chinese and English.
- */
-@Slf4j
-public class DemoCompatibleHeaderDataListener extends AnalysisEventListener {
-
- /**
- * Store data in batches of 100. In practice, you can adjust this number based on your needs.
- * After storing, clear the list to facilitate memory recovery.
- */
- private static final int BATCH_COUNT = 100;
-
- /**
- * Map various header names to their corresponding annotation header information.
- */
- private final Map headerMapping = new HashMap<>(8);
-
- /**
- * Cache data in a list.
- */
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- {
- // Initialize the header mapping with examples.
- headerMapping.put("字符串标题", "String");
- headerMapping.put("日期标题", "Date");
- headerMapping.put("数字标题", "DoubleData");
- }
-
- /**
- * This method will be called for each row of headers.
- *
- * @param headMap A map containing the header information.
- * @param context The analysis context.
- */
- @Override
- public void invokeHead(Map> headMap, AnalysisContext context) {
- log.info("Parsed one header row:{}", JSON.toJSONString(headMap));
- headMap.forEach((key, value) -> {
- // Here, a header mapping relationship is established. You can customize this logic as needed,
- // such as case conversion, suffix removal, space deletion, etc.
- String stringValue = value.getStringValue();
- value.setStringValue(headerMapping.getOrDefault(stringValue, stringValue));
- });
- }
-
- /**
- * This method is called for each parsed data row.
- *
- * @param data One row of data. It is the same as {@link AnalysisContext#readRowHolder()}.
- * @param context The analysis context.
- */
- @Override
- public void invoke(DemoCompatibleHeaderData data, AnalysisContext context) {
- log.info("Parsed one data row:{}", JSON.toJSONString(data));
- cachedDataList.add(data);
- // When the cached data reaches BATCH_COUNT, store it to prevent OOM issues with large datasets.
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- // Clear the list after storage.
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- /**
- * Called when all data has been analyzed.
- *
- * @param context The analysis context.
- */
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- log.info("All data parsing completed!");
- }
-
- /**
- * Simulates saving data to a database.
- */
- private void saveData() {
- log.info("{} rows of data, starting to save to the database!", cachedDataList.size());
- log.info("Data saved successfully to the database!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoData.java
deleted file mode 100644
index ce90bf84b..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoData.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * 基础数据类.这里的排序和excel里面的排序一致
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class DemoData {
- private String string;
- private Date date;
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataAnother.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataAnother.java
deleted file mode 100644
index ec9b41fc0..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataAnother.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-@Getter
-@Setter
-@EqualsAndHashCode
-@ToString
-public class DemoDataAnother {
- private String strA;
- private String strB;
- private String strC;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataListener.java
deleted file mode 100644
index d1574de19..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoDataListener.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-
-/**
- * Template reading class
- *
- *
- */
-// An important point is that DemoDataListener should not be managed by Spring.
-// It needs to be newly created each time an Excel file is read.
-// If Spring is used inside, it can be passed through the constructor.
-@Slf4j
-public class DemoDataListener implements ReadListener {
-
- /**
- * Store data in the database every 5 records. In actual use, it can be 100 records,
- * and then clear the list to facilitate memory recycling.
- */
- private static final int BATCH_COUNT = 100;
- /**
- * Cached data
- */
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- /**
- * Assume this is a DAO. Of course, if there is business logic, this can also be a service.
- * If the data does not need to be stored, this object is useless.
- */
- private DemoDAO demoDAO;
-
- public DemoDataListener() {
- // This is a demo, so a new instance is created here. In actual use with Spring,
- // please use the constructor with parameters below.
- demoDAO = new DemoDAO();
- }
-
- /**
- * If Spring is used, please use this constructor. When creating a Listener, the class managed by Spring needs to be passed in.
- *
- * @param demoDAO
- */
- public DemoDataListener(DemoDAO demoDAO) {
- this.demoDAO = demoDAO;
- }
-
- /**
- * This method will be called for each data parsed.
- *
- * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()}
- * @param context
- */
- @Override
- public void invoke(DemoData data, AnalysisContext context) {
- log.info("Parsed one row of data: {}", JSON.toJSONString(data));
- cachedDataList.add(data);
- // When the number of records reaches BATCH_COUNT, the data needs to be stored in the database to prevent tens
- // of
- // thousands of records from being held in memory, which can easily cause OutOfMemoryError (OOM).
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- // Clear the list after storage
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- /**
- * This method will be called after all data has been parsed.
- *
- * @param context
- */
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- // Data needs to be saved here to ensure that any remaining data is stored in the database.
- saveData();
- log.info("All data has been parsed!");
- }
-
- /**
- * Store data in the database
- */
- private void saveData() {
- log.info("{} records are being stored in the database!", cachedDataList.size());
- demoDAO.save(cachedDataList);
- log.info("Data has been successfully stored in the database!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExtraListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExtraListener.java
deleted file mode 100644
index cade0789d..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoExtraListener.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.metadata.CellExtra;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-import org.junit.jupiter.api.Assertions;
-
-/**
- * Listener to read cell comments, hyperlinks, and merged cells.
- *
- *
- **/
-@Slf4j
-public class DemoExtraListener implements ReadListener {
-
- @Override
- public void invoke(DemoExtraData data, AnalysisContext context) {}
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {}
-
- @Override
- public void extra(CellExtra extra, AnalysisContext context) {
- log.info("Read an extra piece of information: {}", JSON.toJSONString(extra));
- switch (extra.getType()) {
- case COMMENT:
- log.info(
- "The extra information is a comment, at rowIndex:{}, columnIndex:{}, content:{}",
- extra.getRowIndex(),
- extra.getColumnIndex(),
- extra.getText());
- break;
- case HYPERLINK:
- if ("Sheet1!A1".equals(extra.getText())) {
- log.info(
- "The extra information is a hyperlink, at rowIndex:{}, columnIndex:{}, content:{}",
- extra.getRowIndex(),
- extra.getColumnIndex(),
- extra.getText());
- } else if ("Sheet2!A1".equals(extra.getText())) {
- log.info(
- "The extra information is a hyperlink, covering a range, firstRowIndex:{}, firstColumnIndex:{}, "
- + "lastRowIndex:{}, lastColumnIndex:{}, content:{}",
- extra.getFirstRowIndex(),
- extra.getFirstColumnIndex(),
- extra.getLastRowIndex(),
- extra.getLastColumnIndex(),
- extra.getText());
- } else {
- Assertions.fail("Unknown hyperlink!");
- }
- break;
- case MERGE:
- log.info(
- "The extra information is a merged cell, covering a range, firstRowIndex:{}, firstColumnIndex:{}, "
- + "lastRowIndex:{}, lastColumnIndex:{}",
- extra.getFirstRowIndex(),
- extra.getFirstColumnIndex(),
- extra.getLastRowIndex(),
- extra.getLastColumnIndex());
- break;
- default:
- }
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoHeadDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoHeadDataListener.java
deleted file mode 100644
index e06b90d58..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/DemoHeadDataListener.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.List;
-import java.util.Map;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.exception.ExcelDataConvertException;
-import org.apache.fesod.sheet.metadata.data.ReadCellData;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-
-/**
- * Reading headers
- *
- *
- */
-@Slf4j
-public class DemoHeadDataListener implements ReadListener {
-
- /**
- * Save to the database every 5 records. In actual use, you might use 100 records,
- * then clear the list to facilitate memory recycling.
- */
- private static final int BATCH_COUNT = 5;
-
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- /**
- * This method is called when a conversion exception or other exceptions occur.
- * Throwing an exception will stop the reading process. If no exception is thrown here,
- * the reading will continue to the next row.
- *
- * @param exception The exception that occurred.
- * @param context The analysis context.
- * @throws Exception If an exception is thrown to stop reading.
- */
- @Override
- public void onException(Exception exception, AnalysisContext context) {
- log.error("Parsing failed, but continue parsing the next row: {}", exception.getMessage());
- if (exception instanceof ExcelDataConvertException) {
- ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
- log.error(
- "Row {}, Column {} parsing exception, data is: {}",
- excelDataConvertException.getRowIndex(),
- excelDataConvertException.getColumnIndex(),
- excelDataConvertException.getCellData());
- }
- }
-
- /**
- * This method is called for each header row.
- *
- * @param headMap The header data as a map.
- * @param context The analysis context.
- */
- @Override
- public void invokeHead(Map> headMap, AnalysisContext context) {
- log.info("Parsed a header row: {}", JSON.toJSONString(headMap));
- // If you want to convert it to a Map:
- // Solution 1: Do not implement ReadListener, but extend AnalysisEventListener.
- // Solution 2: Call ConverterUtils.convertToStringMap(headMap, context) to convert automatically.
- }
-
- @Override
- public void invoke(DemoData data, AnalysisContext context) {
- log.info("Parsed a piece of data: {}", JSON.toJSONString(data));
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- log.info("All data has been parsed and processed!");
- }
-
- /**
- * Simulate saving data to the database.
- */
- private void saveData() {
- log.info("Saving {} records to the database!", cachedDataList.size());
- log.info("Data saved to the database successfully!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/GenericHeaderTypeDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/GenericHeaderTypeDataListener.java
deleted file mode 100644
index 6dd9b9a02..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/GenericHeaderTypeDataListener.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-
-/**
- * A data listener example that specifies the header type through generics.
- *
- * @param
- */
-@Slf4j
-public class GenericHeaderTypeDataListener implements ReadListener {
-
- private final Class headerClass;
-
- private GenericHeaderTypeDataListener(Class headerClass) {
- this.headerClass = headerClass;
- }
-
- @Override
- public void invoke(T data, AnalysisContext context) {
- log.info("data:{}", data);
- // Execute business logic
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- // Perform cleanup tasks
- }
-
- public static GenericHeaderTypeDataListener build(Class excelHeaderClass) {
- return new GenericHeaderTypeDataListener<>(excelHeaderClass);
- }
-
- public Class getHeaderClass() {
- return headerClass;
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameDataListener.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameDataListener.java
deleted file mode 100644
index 0335c0456..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/IndexOrNameDataListener.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.event.AnalysisEventListener;
-
-/**
- * Template reading class
- *
- *
- */
-@Slf4j
-public class IndexOrNameDataListener extends AnalysisEventListener {
-
- /**
- * Store data in the database every 5 records. In actual use, it can be 100 records,
- * and then clear the list to facilitate memory recycling.
- */
- private static final int BATCH_COUNT = 5;
-
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- @Override
- public void invoke(IndexOrNameData data, AnalysisContext context) {
- log.info("Parsed one row of data: {}", JSON.toJSONString(data));
- cachedDataList.add(data);
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- log.info("All data has been parsed!");
- }
-
- /**
- * Store data in the database
- */
- private void saveData() {
- log.info("{} records are being stored in the database!", cachedDataList.size());
- log.info("Data has been successfully stored in the database!");
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ReadTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ReadTest.java
deleted file mode 100644
index aba7545e8..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/ReadTest.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import com.alibaba.fastjson2.JSON;
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.ExcelReader;
-import org.apache.fesod.sheet.FesodSheet;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
-import org.apache.fesod.sheet.annotation.format.NumberFormat;
-import org.apache.fesod.sheet.context.AnalysisContext;
-import org.apache.fesod.sheet.converters.DefaultConverterLoader;
-import org.apache.fesod.sheet.enums.CellExtraTypeEnum;
-import org.apache.fesod.sheet.read.listener.PageReadListener;
-import org.apache.fesod.sheet.read.listener.ReadListener;
-import org.apache.fesod.sheet.read.metadata.ReadSheet;
-import org.apache.fesod.sheet.read.metadata.holder.csv.CsvReadWorkbookHolder;
-import org.apache.fesod.sheet.util.TestFileUtil;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-/**
- * Common approaches for reading Excel files
- *
- *
- */
-@Slf4j
-public class ReadTest {
-
- /**
- * Simplest way to read
- *
- * 1. Create an entity class corresponding to the Excel data structure. Refer to {@link DemoData}.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link DemoDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void simpleRead() {
- // Approach 1: JDK8+, no need to create a separate DemoDataListener
- // since: 3.0.0-beta1
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read the data, then read the first sheet. The file stream will be automatically closed.
- // By default, it reads 100 rows at a time. You can process the data directly.
- // The number of rows to read can be set in the constructor of `PageReadListener`.
- FesodSheet.read(fileName, DemoData.class, new PageReadListener(dataList -> {
- for (DemoData demoData : dataList) {
- log.info("Reading a row of data: {}", JSON.toJSONString(demoData));
- }
- }))
- .numRows(2)
- .sheet()
- .doRead();
-
- // Approach 2:
- // Anonymous inner class, no need to create a separate DemoDataListener
- fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read the data, then read the first sheet. The file stream will be automatically closed.
- FesodSheet.read(fileName, DemoData.class, new ReadListener() {
- /**
- * Batch size for caching data
- */
- public static final int BATCH_COUNT = 100;
- /**
- * Temporary storage
- */
- private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
-
- @Override
- public void invoke(DemoData data, AnalysisContext context) {
- cachedDataList.add(data);
- if (cachedDataList.size() >= BATCH_COUNT) {
- saveData();
- // Clear the list after saving
- cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
- }
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- saveData();
- }
-
- /**
- * Simulate saving data to the database
- */
- private void saveData() {
- log.info("Saving {} rows of data to the database!", cachedDataList.size());
- log.info("Data saved successfully!");
- }
- })
- .sheet()
- .doRead();
-
- // Important note: DemoDataListener should not be managed by Spring. It needs to be instantiated every time you
- // read an Excel file.
- // Approach 3:
- fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read the data, then read the first sheet. The file stream will be automatically closed.
- FesodSheet.read(fileName, DemoData.class, new DemoDataListener())
- .sheet()
- .doRead();
-
- // Approach 4
- fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // One reader per file
- try (ExcelReader excelReader = FesodSheet.read(fileName, DemoData.class, new DemoDataListener())
- .build()) {
- // Build a sheet. You can specify the name or index.
- ReadSheet readSheet = FesodSheet.readSheet(0).build();
- readSheet.setNumRows(2);
- // Read a single sheet
- excelReader.read(readSheet);
- }
- }
-
- @Test
- public void genericHeaderTypeRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "generic-demo.xlsx";
- // Simulate obtaining the Excel header's Class> object through any possible means
- Class> excelHeaderClass = DemoDataAnother.class;
- FesodSheet.read(fileName, excelHeaderClass, GenericHeaderTypeDataListener.build(excelHeaderClass))
- .sheet()
- .doRead();
- }
-
- /**
- * Specify column indexes or names
- *
- * 1. Create an entity class corresponding to the Excel data structure and use the {@link ExcelProperty} annotation. Refer to {@link IndexOrNameData}.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link IndexOrNameDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void indexOrNameRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // By default, read the first sheet
- FesodSheet.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener())
- .numRows(1)
- .sheet()
- .doRead();
- }
-
- /**
- * Read multiple or all sheets. Note that a sheet cannot be read multiple times; multiple reads require re-reading the file.
- *
- * 1. Create an entity class corresponding to the Excel data structure. Refer to {@link DemoData}.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link DemoDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void repeatedRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Read all sheets
- // Note: The `doAfterAllAnalysed` method of DemoDataListener will be called once after each sheet is read.
- // All sheets will write to the same DemoDataListener.
- FesodSheet.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
-
- // Read some sheets
- fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
-
- // Method 1
- try (ExcelReader excelReader = FesodSheet.read(fileName).build()) {
- // For simplicity, the same head and Listener are registered here.
- // In actual use, different Listeners must be used.
- ReadSheet readSheet1 = FesodSheet.readSheet(0)
- .head(DemoData.class)
- .registerReadListener(new DemoDataListener())
- .build();
- ReadSheet readSheet2 = FesodSheet.readSheet(1)
- .head(DemoData.class)
- .registerReadListener(new DemoDataListener())
- .build();
- // Note: All sheets (sheet1 and sheet2) must be passed together.
- // Otherwise, for Excel 2003 files, the same sheet may be read multiple times, wasting performance.
- excelReader.read(readSheet1, readSheet2);
- }
- }
-
- /**
- * Date, number, or custom format conversion
- *
- * 1. Create an entity class corresponding to the Excel data structure. Refer to {@link ConverterData}.
- * Annotations such as {@link DateTimeFormat}, {@link NumberFormat}, or custom annotations can be used.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link ConverterDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void converterRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, ConverterData.class, new ConverterDataListener())
- // Note: We can also register a custom converter using `registerConverter`.
- // However, this converter will be global, and all fields with Java type `String` and Excel type
- // `String` will use this converter.
- // If you want to use it for a single field, specify the converter using `@ExcelProperty`.
- // .registerConverter(new CustomStringStringConverter())
- // Read the sheet
- .sheet()
- .doRead();
- }
-
- /**
- * Multi-row header
- *
- *
- * 1. Create an entity class corresponding to the Excel data structure. Refer to {@link DemoData}.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link DemoDataListener}.
- *
- * 3. Set the `headRowNumber` parameter, then read. Note that if `headRowNumber` is not specified,
- * the number of rows will be determined by the number of headers in the `@ExcelProperty#value()` of the class you provide.
- * If no class is provided, the default is 1. Of course, if you specify `headRowNumber`, it will be used regardless of whether a class is provided.
- */
- @Test
- public void complexHeaderRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, DemoData.class, new DemoDataListener())
- .sheet()
- // Set to 1 here because the header is one row. For multi-row headers, set to other values.
- // You can also omit this, as the default behavior will parse based on DemoData, which does not specify
- // a header, meaning the default is 1 row.
- .headRowNumber(1)
- .doRead();
- }
-
- /**
- * Method to read Excel files with headers that support compatibility, such as case sensitivity or simultaneous
- * support for Chinese and English headers.
- *
- *
- * 1. Create an entity object corresponding to the Excel data structure. Refer to {@link DemoCompatibleHeaderData}
- * for implementation details.
- *
- *
- *
- * 2. Since Fesod reads the Excel file row by row by default, you need to create a listener that handles each
- * row's data accordingly. Refer to {@link DemoCompatibleHeaderDataListener} for implementation details. In this
- * listener, you should override the `invokeHead` method to transform the uploaded headers as needed.
- *
- *
- *
- * 3. Simply proceed to read the file.
- *
- */
- @Test
- public void compatibleHeaderRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class used for reading and choose to read the first sheet.
- FesodSheet.read(fileName, DemoCompatibleHeaderData.class, new DemoCompatibleHeaderDataListener())
- .sheet()
- .doRead();
- }
-
- /**
- * Read header data
- *
- *
- * 1. Create an entity object corresponding to the Excel data structure. Refer to {@link DemoData}.
- *
- * 2. Since Fesod reads Excel files row by row, you need to create a callback listener for each row. Refer to {@link DemoHeadDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void headerRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, DemoData.class, new DemoHeadDataListener())
- .sheet()
- .doRead();
- }
-
- /**
- * Additional information (comments, hyperlinks, merged cell information)
- *
- * Since it is stream-based reading, it is not possible to directly read additional information when reading cell data.
- * Therefore, only notifications of which cells contain additional information can be provided at the end.
- *
- *
- * 1. Create an entity object corresponding to the Excel data structure. Refer to {@link DemoExtraData}.
- *
- * 2. Since Fesod reads Excel files row by row by default, you need to create a callback listener for each row. Refer to {@link DemoExtraListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void extraRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "extra.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, DemoExtraData.class, new DemoExtraListener())
- // Read comments (default is not to read)
- .extraRead(CellExtraTypeEnum.COMMENT)
- // Read hyperlinks (default is not to read)
- .extraRead(CellExtraTypeEnum.HYPERLINK)
- // Read merged cell information (default is not to read)
- .extraRead(CellExtraTypeEnum.MERGE)
- .sheet()
- .doRead();
- }
-
- /**
- * Read formulas and cell types
- *
- *
- * 1. Create an entity object corresponding to the Excel data structure. Refer to {@link CellDataReadDemoData}.
- *
- * 2. Since Fesod reads Excel files row by row by default, you need to create a callback listener for each row. Refer to {@link CellDataDemoHeadDataListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void cellDataRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener())
- .sheet()
- .doRead();
- }
-
- /**
- * Exception handling for data conversion, etc.
- *
- *
- * 1. Create an entity object corresponding to the Excel data structure. Refer to {@link ExceptionDemoData}.
- *
- * 2. Since Fesod reads Excel files row by row by default, you need to create a callback listener for each row. Refer to {@link DemoExceptionListener}.
- *
- * 3. Directly read the file.
- */
- @Test
- public void exceptionRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read, then read the first sheet
- FesodSheet.read(fileName, ExceptionDemoData.class, new DemoExceptionListener())
- .sheet()
- .doRead();
- }
-
- /**
- * Synchronous return is not recommended, as it will store data in memory if the data volume is large.
- */
- @Test
- public void synchronousRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Specify the class to read, then read the first sheet. Synchronous reading will automatically finish.
- List list =
- FesodSheet.read(fileName).head(DemoData.class).sheet().doReadSync();
- for (DemoData data : list) {
- log.info("Read data:{}", JSON.toJSONString(data));
- }
-
- // Alternatively, you can read without specifying a class, returning a list, then read the first sheet.
- // Synchronous reading will automatically finish.
- List> listMap = FesodSheet.read(fileName).sheet().doReadSync();
- for (Map data : listMap) {
- // Return key-value pairs for each data item, representing the column index and its value.
- log.info("Read data:{}", JSON.toJSONString(data));
- }
- }
-
- /**
- * Reading without creating objects
- */
- @Test
- public void noModelRead() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- // Simply read the first sheet. Synchronous reading will automatically finish.
- FesodSheet.read(fileName, new NoModelDataListener()).sheet().doRead();
- }
-
- /**
- * Custom modification of CSV configuration
- */
- @Nested
- class ReadCsvFormat {
-
- @Test
- void asNormalJavaBean() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.csv";
- try (ExcelReader excelReader = FesodSheet.read(fileName, DemoData.class, new DemoDataListener())
- .build()) {
- // Check if it is a CSV file
- if (excelReader.analysisContext().readWorkbookHolder() instanceof CsvReadWorkbookHolder) {
- CsvReadWorkbookHolder csvReadWorkbookHolder = (CsvReadWorkbookHolder)
- excelReader.analysisContext().readWorkbookHolder();
- // Set to comma-separated (default is also comma-separated)
- // Note: `withDelimiter` will regenerate the format, so it needs to be set back.
- csvReadWorkbookHolder.setCsvFormat(
- csvReadWorkbookHolder.getCsvFormat().withDelimiter(','));
- }
-
- // Get all sheets
- List readSheetList = excelReader.excelExecutor().sheetList();
- // If you only want to read the first sheet, you can pass the parameter accordingly.
- // ReadSheet readSheet = FesodSheet.readSheet(0).build();
- excelReader.read(readSheetList);
- }
- }
-
- @Test
- void asChainedAccessorsJavaBean() {
- String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.csv";
- try (ExcelReader excelReader = FesodSheet.read(
- fileName, DemoChainAccessorsData.class, new ReadListener() {
- @Override
- public void invoke(DemoChainAccessorsData data, AnalysisContext context) {
- Assertions.assertNotNull(data.getString());
- Assertions.assertNotNull(data.getDate());
- Assertions.assertNotNull(data.getDoubleData());
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {}
- })
- .build()) {
- excelReader.readAll();
- }
- }
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/Sample.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/Sample.java
deleted file mode 100644
index c76f40396..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/read/Sample.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.read;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-@Getter
-@Setter
-@EqualsAndHashCode
-@NoArgsConstructor
-public class Sample {
-
- private String header;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/web/WebTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/web/WebTest.java
deleted file mode 100644
index f63490edc..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/web/WebTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.web;
-
-import com.alibaba.fastjson2.JSON;
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.common.util.MapUtils;
-import org.apache.fesod.sheet.FesodSheet;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.multipart.MultipartFile;
-
-/**
- * Web read and write examples
- *
- *
- **/
-@Controller
-public class WebTest {
-
- @Autowired
- private UploadDAO uploadDAO;
-
- /**
- * File download (returns an Excel with partial data if failed)
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DownloadData}
- *
- * 2. Set the return parameters
- *
- * 3. Write directly. Note that the OutputStream will be automatically closed when finish is called. It's fine to close the stream outside as well
- */
- @GetMapping("download")
- public void download(HttpServletResponse response) throws IOException {
- // Note: Some students reported that using Swagger causes various issues, please use browser directly or use
- // Postman
- response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
- response.setCharacterEncoding("utf-8");
- // Here URLEncoder.encode can prevent Chinese character encoding issues, which is unrelated to Fesod
- String fileName = URLEncoder.encode("test", "UTF-8").replaceAll("\\+", "%20");
- response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
-
- FesodSheet.write(response.getOutputStream(), DownloadData.class)
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * File download that returns JSON when failed (by default, returns an Excel with partial data when failed)
- */
- @GetMapping("downloadFailedUsingJson")
- public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
- // Note: Some students reported that using Swagger causes various issues, please use browser directly or use
- // Postman
- try {
- response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
- response.setCharacterEncoding("utf-8");
- // Here URLEncoder.encode can prevent Chinese character encoding issues, which is unrelated to Fesod
- String fileName = URLEncoder.encode("test", "UTF-8").replaceAll("\\+", "%20");
- response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
- // Here we need to set not to close the stream
- FesodSheet.write(response.getOutputStream(), DownloadData.class)
- .autoCloseStream(Boolean.FALSE)
- .sheet("Template")
- .doWrite(data());
- } catch (Exception e) {
- // Reset response
- response.reset();
- response.setContentType("application/json");
- response.setCharacterEncoding("utf-8");
- Map map = MapUtils.newHashMap();
- map.put("status", "failure");
- map.put("message", "Failed to download file: " + e.getMessage());
- response.getWriter().println(JSON.toJSONString(map));
- }
- }
-
- /**
- * File upload
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link UploadData}
- *
- * 2. Since Excel is read row by row by default, you need to create a row-by-row callback listener for Excel. Refer to {@link UploadDataListener}
- *
- * 3. Read directly
- */
- @PostMapping("upload")
- @ResponseBody
- public String upload(MultipartFile file) throws IOException {
- FesodSheet.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO))
- .sheet()
- .doRead();
- return "success";
- }
-
- private List data() {
- List list = ListUtils.newArrayList();
- for (int i = 0; i < 10; i++) {
- DownloadData data = new DownloadData();
- data.setString("String" + 0);
- data.setDate(new Date());
- data.setDoubleData(0.56);
- list.add(data);
- }
- return list;
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ColorDemoData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ColorDemoData.java
deleted file mode 100644
index 0f80e71de..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ColorDemoData.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.write.style.HeadFontStyle;
-import org.apache.poi.ss.usermodel.Font;
-
-/**
- * Basic data class for test color
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-@AllArgsConstructor
-@NoArgsConstructor
-public class ColorDemoData {
-
- @ExcelProperty("Name")
- private String name;
-
- @ExcelProperty("Age")
- @HeadFontStyle(color = Font.COLOR_RED)
- private Integer age;
-
- @ExcelProperty("Sex")
- private String sex;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ComplexHeadData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ComplexHeadData.java
deleted file mode 100644
index 4ed4c4ddf..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ComplexHeadData.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-
-/**
- * Complex header data. The final effect is a main title on the first row, and categories on the second row.
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class ComplexHeadData {
- @ExcelProperty({"Main Title", "String Title"})
- private String string;
-
- @ExcelProperty({"Main Title", "Date Title"})
- private Date date;
-
- @ExcelProperty({"Main Title", "Double Title"})
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ConverterData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ConverterData.java
deleted file mode 100644
index 9448d3b1d..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ConverterData.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
-import org.apache.fesod.sheet.annotation.format.NumberFormat;
-
-/**
- * Basic data class. The sorting here is consistent with the sorting in Excel.
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class ConverterData {
- /**
- * I want to add "Custom:" to the beginning of all strings.
- */
- @ExcelProperty(value = "String Title", converter = CustomStringStringConverter.class)
- private String string;
- /**
- * I want to write to Excel using the format "yyyy-MM-dd HH:mm:ss"
- */
- @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
- @ExcelProperty("Date Title")
- private Date date;
- /**
- * I want to write to Excel using percentage format.
- */
- @NumberFormat("#.##%")
- @ExcelProperty(value = "Double Title")
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomStringStringConverter.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomStringStringConverter.java
deleted file mode 100644
index bce1f630c..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/CustomStringStringConverter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import org.apache.fesod.sheet.converters.Converter;
-import org.apache.fesod.sheet.converters.ReadConverterContext;
-import org.apache.fesod.sheet.converters.WriteConverterContext;
-import org.apache.fesod.sheet.enums.CellDataTypeEnum;
-import org.apache.fesod.sheet.metadata.data.WriteCellData;
-
-/**
- * String and string converter
- *
- *
- */
-public class CustomStringStringConverter implements Converter {
- @Override
- public Class> supportJavaTypeKey() {
- return String.class;
- }
-
- @Override
- public CellDataTypeEnum supportExcelTypeKey() {
- return CellDataTypeEnum.STRING;
- }
-
- /**
- * 这里是读的时候会调用 不用管
- *
- * @return
- */
- @Override
- public String convertToJavaData(ReadConverterContext> context) {
- return context.getReadCellData().getStringValue();
- }
-
- /**
- * 这里是写的时候会调用 不用管
- *
- * @return
- */
- @Override
- public WriteCellData> convertToExcelData(WriteConverterContext context) {
- return new WriteCellData<>("自定义:" + context.getValue());
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/EscapeHexCellWriteHandlerTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/EscapeHexCellWriteHandlerTest.java
deleted file mode 100644
index 0d5d266e5..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/EscapeHexCellWriteHandlerTest.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import org.apache.fesod.sheet.FesodSheet;
-import org.apache.fesod.sheet.support.ExcelTypeEnum;
-import org.apache.fesod.sheet.write.handler.EscapeHexCellWriteHandler;
-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.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-public class EscapeHexCellWriteHandlerTest {
-
- @TempDir
- Path tempDir;
-
- private DemoData createDemoData(String str) {
- DemoData data = new DemoData();
- data.setString(str);
- data.setDate(new Date());
- data.setDoubleData(123.45);
- data.setIgnore("ignoreMe");
- return data;
- }
-
- @Test
- public void testEscapeHex_xlsx_singleHex() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_xB9f0_"));
-
- File file = tempDir.resolve("testEscapeHex.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Verify the result
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1); // Data row (header is row 0)
- Cell cell = row.getCell(0); // String column
- String actualValue = cell.getStringCellValue();
- System.out.println("XLSX result: " + actualValue);
- Assertions.assertNotEquals("_x005F_xB9f0_", actualValue, "xlsx should not escape _xB9f0_ to _x005F_xB9f0_");
- }
- }
-
- @Test
- public void testEscapeHex_xls_singleHex() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_xB9f0_"));
-
- File file = tempDir.resolve("testEscapeHex.xls").toFile();
- FesodSheet.write(file, DemoData.class)
- .excelType(ExcelTypeEnum.XLS)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Verify the result
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- Assertions.assertNotEquals(
- "_x005F_xB9f0_", cell.getStringCellValue(), "xls should not escape _xB9f0_ to _x005F_xB9f0_");
- }
- }
-
- @Test
- public void testEscapeHex_csv_singleHex() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_xB9f0_"));
-
- File file = tempDir.resolve("testEscapeHex.csv").toFile();
- FesodSheet.write(file, DemoData.class)
- .excelType(ExcelTypeEnum.CSV)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Verify the result
- try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
- reader.readLine(); // Skip header
- String dataLine = reader.readLine();
- Assertions.assertNotNull(dataLine);
- Assertions.assertFalse(
- dataLine.contains("_x005F_xB9f0_"),
- "csv should not contain escaped _x005F_xB9f0_, but was: " + dataLine);
- }
- }
-
- @Test
- public void testEscapeHex_multipleHexInOneString() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_xB9f0_ and _x1234_ and _xABCD_"));
-
- File file = tempDir.resolve("testMultipleHex.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- String expected = "_xB9f0_ and _x1234_ and _xABCD_";
- Assertions.assertEquals(expected, cell.getStringCellValue(), "Multiple hex patterns should all be escaped");
- }
- }
-
- @Test
- public void testEscapeHex_noHexPattern() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("normalString"));
-
- File file = tempDir.resolve("testNoHex.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- Assertions.assertEquals("normalString", cell.getStringCellValue(), "Normal strings should not be modified");
- }
- }
-
- @Test
- public void testEscapeHex_partialHexPattern() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_x123_ _xABC_ _x12345_")); // Invalid patterns
-
- File file = tempDir.resolve("testPartialHex.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- Assertions.assertEquals(
- "_x123_ _xABC_ _x12345_", cell.getStringCellValue(), "Invalid hex patterns should not be modified");
- }
- }
-
- @Test
- public void testEscapeHex_mixedValidAndInvalidPatterns() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_x1234_ _x123_ _xABCD_ _xGHIJ_"));
-
- File file = tempDir.resolve("testMixedHex.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- String expected = "_x1234_ _x123_ _xABCD_ _xGHIJ_";
- Assertions.assertEquals(expected, cell.getStringCellValue(), "Only valid hex patterns should be escaped");
- }
- }
-
- @Test
- public void testEscapeHex_emptyAndNullStrings() throws Exception {
- List list = new ArrayList<>();
- DemoData data1 = createDemoData("");
- DemoData data2 = createDemoData(null);
- list.add(data1);
- list.add(data2);
-
- File file = tempDir.resolve("testEmptyNull.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
-
- // Check empty string
- Row row1 = sheet.getRow(1);
- Cell cell1 = row1.getCell(0);
- Assertions.assertEquals("", cell1.getStringCellValue(), "Empty string should remain empty");
-
- // Check null string
- Row row2 = sheet.getRow(2);
- Cell cell2 = row2.getCell(0);
- if (cell2 != null) {
- Assertions.assertEquals("", cell2.getStringCellValue(), "Null string should be handled gracefully");
- }
- }
- }
-
- @Test
- public void testEscapeHex_caseInsensitiveHex() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_x1a2B_ _XC3d4_ _x9F8e_"));
-
- File file = tempDir.resolve("testCaseInsensitive.xlsx").toFile();
- FesodSheet.write(file, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- try (Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file)) {
- Sheet sheet = workbook.getSheetAt(0);
- Row row = sheet.getRow(1);
- Cell cell = row.getCell(0);
- String expected = "_x1a2B_ _XC3d4_ _x9F8e_";
- Assertions.assertEquals(
- expected, cell.getStringCellValue(), "Case-sensitive hex patterns should be handled correctly");
- }
- }
-
- @Test
- public void testEscapeHex_multipleDifferentFormats() throws Exception {
- List list = new ArrayList<>();
- list.add(createDemoData("_xB9f0_"));
-
- // Test xlsx
- File xlsxFile = tempDir.resolve("testFormats.xlsx").toFile();
- FesodSheet.write(xlsxFile, DemoData.class)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Test xls
- File xlsFile = tempDir.resolve("testFormats.xls").toFile();
- FesodSheet.write(xlsFile, DemoData.class)
- .excelType(ExcelTypeEnum.XLS)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Test csv
- File csvFile = tempDir.resolve("testFormats.csv").toFile();
- FesodSheet.write(csvFile, DemoData.class)
- .excelType(ExcelTypeEnum.CSV)
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .sheet("TestSheet")
- .doWrite(list);
-
- // Verify all formats produce the same escaped result
- try (Workbook xlsxWorkbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(xlsxFile);
- Workbook xlsWorkbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(xlsFile);
- BufferedReader csvReader = new BufferedReader(new FileReader(csvFile))) {
-
- // Check xlsx
- String xlsxValue = xlsxWorkbook.getSheetAt(0).getRow(1).getCell(0).getStringCellValue();
- Assertions.assertEquals("_xB9f0_", xlsxValue);
-
- // Check xls
- String xlsValue = xlsWorkbook.getSheetAt(0).getRow(1).getCell(0).getStringCellValue();
- Assertions.assertEquals("_xB9f0_", xlsValue);
-
- // Check csv
- csvReader.readLine(); // Skip header
- String csvLine = csvReader.readLine();
- Assertions.assertTrue(csvLine.contains("_xB9f0_"));
- Assertions.assertFalse(csvLine.contains("_x005F_xB9f0_"));
-
- // All formats should produce the same result
- Assertions.assertEquals(xlsxValue, xlsValue, "xlsx, csv and xls should produce the same escaped result");
- }
- }
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDataWithAnnotation.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDataWithAnnotation.java
deleted file mode 100644
index 59d7b4ace..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDataWithAnnotation.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
-import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
-import org.apache.fesod.sheet.converters.string.StringImageConverter;
-
-/**
- * 图片导出类
- */
-@Getter
-@Setter
-@EqualsAndHashCode
-@ContentRowHeight(100)
-@ColumnWidth(100 / 8)
-public class ImageDataWithAnnotation {
- private File file;
- private InputStream inputStream;
- /**
- * 如果string类型 必须指定转换器,string默认转换成string
- */
- @ExcelProperty(converter = StringImageConverter.class)
- private String string;
-
- private byte[] byteArray;
- /**
- * 根据url导出
- */
- private URL url;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDemoData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDemoData.java
deleted file mode 100644
index 722d3ee50..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/ImageDemoData.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
-import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
-import org.apache.fesod.sheet.converters.string.StringImageConverter;
-import org.apache.fesod.sheet.metadata.data.WriteCellData;
-
-/**
- * Image export class
- */
-@Getter
-@Setter
-@EqualsAndHashCode
-@ContentRowHeight(100)
-@ColumnWidth(100 / 8)
-public class ImageDemoData {
- private File file;
- private InputStream inputStream;
- /**
- * If it is a String type, a converter must be specified; String is converted to String by default.
- */
- @ExcelProperty(converter = StringImageConverter.class)
- private String string;
-
- private byte[] byteArray;
- /**
- * Export by URL
- *
- * @since 2.1.1
- */
- private URL url;
-
- /**
- * Export by file and set the export location.
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData writeCellDataFile;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/IndexData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/IndexData.java
deleted file mode 100644
index f4e6129d2..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/IndexData.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-
-/**
- * Basic data class
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class IndexData {
- @ExcelProperty(value = "String Title", index = 0)
- private String string;
-
- @ExcelProperty(value = "Date Title", index = 1)
- private Date date;
- /**
- * Setting index to 3 will result in the second column being empty.
- */
- @ExcelProperty(value = "Double Title", index = 3)
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/LongestMatchColumnWidthData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/LongestMatchColumnWidthData.java
deleted file mode 100644
index b3ae2ea28..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/LongestMatchColumnWidthData.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-
-/**
- * Basic data class
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-public class LongestMatchColumnWidthData {
- @ExcelProperty("String Title")
- private String string;
-
- @ExcelProperty("Date Title is very long Date Title is very long")
- private Date date;
-
- @ExcelProperty("Double")
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WidthAndHeightData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WidthAndHeightData.java
deleted file mode 100644
index 6b7fe8c5c..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WidthAndHeightData.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.util.Date;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
-import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
-import org.apache.fesod.sheet.annotation.write.style.HeadRowHeight;
-
-/**
- * Basic data class
- *
- *
- **/
-@Getter
-@Setter
-@EqualsAndHashCode
-@ContentRowHeight(10)
-@HeadRowHeight(20)
-@ColumnWidth(25)
-public class WidthAndHeightData {
- @ExcelProperty("String Title")
- private String string;
-
- @ExcelProperty("Date Title")
- private Date date;
- /**
- * Width is 50
- */
- @ColumnWidth(50)
- @ExcelProperty("Double Title")
- private Double doubleData;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteCellDemoData.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteCellDemoData.java
deleted file mode 100644
index 02704925a..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteCellDemoData.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.fesod.sheet.metadata.data.WriteCellData;
-
-/**
- * Write via WriteCellData
- *
- *
- */
-@Getter
-@Setter
-@EqualsAndHashCode
-public class WriteCellDemoData {
- /**
- * Hyperlink
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData hyperlink;
-
- /**
- * Comment
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData commentData;
-
- /**
- * Formula
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData formulaData;
-
- /**
- * Specify cell style. Annotations can also be used.
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData writeCellStyle;
-
- /**
- * Specify multiple styles for a single cell
- *
- * @since 3.0.0-beta1
- */
- private WriteCellData richText;
-}
diff --git a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteTest.java b/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteTest.java
deleted file mode 100644
index 0c8c6a061..000000000
--- a/fesod-examples/fesod-sheet-examples/src/test/java/org/apache/fesod/sheet/demo/write/WriteTest.java
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.fesod.sheet.demo.write;
-
-import java.io.File;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.apache.fesod.common.util.BooleanUtils;
-import org.apache.fesod.common.util.ListUtils;
-import org.apache.fesod.sheet.ExcelWriter;
-import org.apache.fesod.sheet.FesodSheet;
-import org.apache.fesod.sheet.annotation.ExcelProperty;
-import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
-import org.apache.fesod.sheet.annotation.format.NumberFormat;
-import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
-import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
-import org.apache.fesod.sheet.annotation.write.style.HeadRowHeight;
-import org.apache.fesod.sheet.enums.CellDataTypeEnum;
-import org.apache.fesod.sheet.metadata.data.CommentData;
-import org.apache.fesod.sheet.metadata.data.FormulaData;
-import org.apache.fesod.sheet.metadata.data.HyperlinkData;
-import org.apache.fesod.sheet.metadata.data.ImageData;
-import org.apache.fesod.sheet.metadata.data.RichTextStringData;
-import org.apache.fesod.sheet.metadata.data.WriteCellData;
-import org.apache.fesod.sheet.util.FileUtils;
-import org.apache.fesod.sheet.util.TestFileUtil;
-import org.apache.fesod.sheet.write.handler.CellWriteHandler;
-import org.apache.fesod.sheet.write.handler.EscapeHexCellWriteHandler;
-import org.apache.fesod.sheet.write.handler.SheetWriteHandler;
-import org.apache.fesod.sheet.write.handler.context.CellWriteHandlerContext;
-import org.apache.fesod.sheet.write.handler.context.SheetWriteHandlerContext;
-import org.apache.fesod.sheet.write.merge.LoopMergeStrategy;
-import org.apache.fesod.sheet.write.metadata.WriteSheet;
-import org.apache.fesod.sheet.write.metadata.WriteTable;
-import org.apache.fesod.sheet.write.metadata.style.WriteCellStyle;
-import org.apache.fesod.sheet.write.metadata.style.WriteFont;
-import org.apache.fesod.sheet.write.style.HorizontalCellStyleStrategy;
-import org.apache.fesod.sheet.write.style.column.LongestMatchColumnWidthStyleStrategy;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.CellStyle;
-import org.apache.poi.ss.usermodel.FillPatternType;
-import org.apache.poi.ss.usermodel.IndexedColors;
-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.SXSSFSheet;
-import org.junit.jupiter.api.Test;
-
-/**
- * Common writing examples
- *
- *
- */
-public class WriteTest {
-
- /**
- * Simple write
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Write directly
- */
- @Test
- public void simpleWrite() {
- // Note: simpleWrite can be used when the data volume is not large (within 5000, depending on the actual
- // situation). For large data volumes, refer to repeated writes.
-
- // Method 1 JDK8+
- // since: 3.0.0-beta1
- String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- // If you want to use Excel 03, pass the excelType parameter.
- FesodSheet.write(fileName, DemoData.class).sheet("Template").doWrite(() -> {
- // Paging query data
- return data();
- });
-
- // Method 2
- fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- // If you want to use Excel 03, pass the excelType parameter.
- FesodSheet.write(fileName, DemoData.class).sheet("Template").doWrite(data());
-
- // Method 3
- fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName, DemoData.class).build()) {
- WriteSheet writeSheet = FesodSheet.writerSheet("Template").build();
- excelWriter.write(data(), writeSheet);
- }
- }
-
- @Test
- public void testEscapeHex() {
- String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
- FesodSheet.write(fileName, DemoData.class)
- .sheet("Template")
- .registerWriteHandler(new EscapeHexCellWriteHandler())
- .doWrite(() -> {
- return dataHex();
- });
- }
-
- /**
- * Export only specified columns based on parameters
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Include or exclude columns as needed
- *
- * 3. Write directly
- */
- @Test
- public void excludeOrIncludeWrite() {
- String fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
- // Note: When using the ExcelProperty annotation, if you want to avoid empty columns, you need to use the
- // 'order' field instead of 'index'. 'order' will ignore empty columns and continue sequentially, while 'index'
- // will not ignore empty columns (it stays in the specified column).
-
- // Based on user input fields, assuming we want to ignore 'date'
- Set excludeColumnFieldNames = new HashSet<>();
- excludeColumnFieldNames.add("date");
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoData.class)
- .excludeColumnFieldNames(excludeColumnFieldNames)
- .sheet("Template")
- .doWrite(data());
-
- fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
- // Based on user input fields, assuming we only want to export 'date'
- Set includeColumnFieldNames = new HashSet<>();
- includeColumnFieldNames.add("date");
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoData.class)
- .includeColumnFieldNames(includeColumnFieldNames)
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Specify columns to write
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link IndexData}
- *
- * 2. Use {@link ExcelProperty} annotation to specify columns to write
- *
- * 3. Write directly
- */
- @Test
- public void indexWrite() {
- String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, IndexData.class).sheet("Template").doWrite(data());
- }
-
- /**
- * Complex header writing
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link ComplexHeadData}
- *
- * 2. Use {@link ExcelProperty} annotation to specify complex headers
- *
- * 3. Write directly
- */
- @Test
- public void complexHeadWrite() {
- String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, ComplexHeadData.class).sheet("Template").doWrite(data());
- }
-
- /**
- * Repeated writes
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link ComplexHeadData}
- *
- * 2. Use {@link ExcelProperty} annotation to specify complex headers
- *
- * 3. Call write multiple times
- */
- @Test
- public void repeatedWrite() {
- // Method 1: Writing to the same sheet
- String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName, DemoData.class).build()) {
- // Note: Create the writeSheet only once if writing to the same sheet
- WriteSheet writeSheet = FesodSheet.writerSheet("Template").build();
- // Call write. Here I called it five times. In actual use, loop based on the total number of pages in the
- // database query.
- for (int i = 0; i < 5; i++) {
- // Paging query data from the database. Here you can query the data for each page.
- List data = data();
- excelWriter.write(data, writeSheet);
- }
- }
-
- // Method 2: Writing to different sheets with the same object
- fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify file
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName, DemoData.class).build()) {
- // Call write. Here I called it five times. In actual use, loop based on the total number of pages in the
- // database query. Eventually it will be written to 5 sheets.
- for (int i = 0; i < 5; i++) {
- // Create writeSheet every time. Note that sheetNo must be specified and sheetName must be different.
- WriteSheet writeSheet =
- FesodSheet.writerSheet(i, "Template" + i).build();
- // Paging query data from the database. Here you can query the data for each page.
- List data = data();
- excelWriter.write(data, writeSheet);
- }
- }
-
- // Method 3: Writing to different sheets with different objects
- fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify file
- try (ExcelWriter excelWriter = FesodSheet.write(fileName).build()) {
- // Call write. Here I called it five times. In actual use, loop based on the total number of pages in the
- // database query. Eventually it will be written to 5 sheets.
- for (int i = 0; i < 5; i++) {
- // Create writeSheet every time. Note that sheetNo must be specified and sheetName must be different.
- // Note that DemoData.class can change each time; I used the same class here for convenience.
- // In reality, it can change every time.
- WriteSheet writeSheet = FesodSheet.writerSheet(i, "Template" + i)
- .head(DemoData.class)
- .build();
- // Paging query data from the database. Here you can query the data for each page.
- List data = data();
- excelWriter.write(data, writeSheet);
- }
- }
- }
-
- /**
- * Date, number, or custom format conversion
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link ConverterData}
- *
- * 2. Use {@link ExcelProperty} with annotations {@link DateTimeFormat}, {@link NumberFormat}, or custom annotations
- *
- * 3. Write directly
- */
- @Test
- public void converterWrite() {
- String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, ConverterData.class).sheet("Template").doWrite(data());
- }
-
- /**
- * Image export
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link ImageDemoData}
- *
- * 2. Write directly
- */
- @Test
- public void imageWrite() throws Exception {
- String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx";
-
- // Note: All images will be loaded into memory. There is no good solution for now. For large numbers of images,
- // it is recommended to:
- // 1. Upload images to OSS or other storage sites: https://www.aliyun.com/product/oss, then verify the link
- // directly
- // 2. Use: https://github.com/coobird/thumbnailator or other tools to compress images
-
- String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";
- try (InputStream inputStream = FileUtils.openInputStream(new File(imagePath))) {
- List list = ListUtils.newArrayList();
- ImageDemoData imageDemoData = new ImageDemoData();
- list.add(imageDemoData);
- // Put five types of images. In actual use, just choose one.
- imageDemoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
- imageDemoData.setFile(new File(imagePath));
- imageDemoData.setString(imagePath);
- imageDemoData.setInputStream(inputStream);
- imageDemoData.setUrl(new URL("https://poi.apache.org/images/project-header.png"));
-
- // Demonstration
- // Need to add extra text
- // And need to add 2 images
- // The first image is on the left
- // The second is on the right and occupies the cell behind it
- WriteCellData writeCellData = new WriteCellData<>();
- imageDemoData.setWriteCellDataFile(writeCellData);
- // Set to EMPTY to indicate no other data is needed
- writeCellData.setType(CellDataTypeEnum.STRING);
- writeCellData.setStringValue("Extra text");
-
- // Can put multiple images
- List imageDataList = new ArrayList<>();
- ImageData imageData = new ImageData();
- imageDataList.add(imageData);
- writeCellData.setImageDataList(imageDataList);
- // Put binary image
- imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath)));
- // Image type
- imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
- // Top, Right, Bottom, Left need padding
- // Similar to CSS margin
- // Tested: cannot set too large. If it exceeds the original cell size, opening it will prompt repair. No
- // good solution found yet.
- imageData.setTop(5);
- imageData.setRight(40);
- imageData.setBottom(5);
- imageData.setLeft(5);
-
- // Put second image
- imageData = new ImageData();
- imageDataList.add(imageData);
- writeCellData.setImageDataList(imageDataList);
- imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath)));
- imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
- imageData.setTop(5);
- imageData.setRight(5);
- imageData.setBottom(5);
- imageData.setLeft(50);
- // Set image position. Assume the target is to cover the current cell and the cell to the right.
- // Start point relative to current cell is 0. Can be omitted.
- imageData.setRelativeFirstRowIndex(0);
- imageData.setRelativeFirstColumnIndex(0);
- imageData.setRelativeLastRowIndex(0);
- // First 3 can be omitted. The following one needs to be written, meaning the end needs to move one cell to
- // the right relative to the current cell.
- // This image will cover the current cell and the next one.
- imageData.setRelativeLastColumnIndex(1);
-
- // Write data
- FesodSheet.write(fileName, ImageDemoData.class).sheet().doWrite(list);
- // If image resource is inaccessible, XLSX format will error: SXSSFWorkbook - Failed to dispose sheet
- // Can consider declaring as XLS format
- // FesodSheet.write(fileName, ImageDemoData.class).excelType(ExcelTypeEnum.XLS).sheet().doWrite(list);
- }
- }
-
- /**
- * Hyperlinks, comments, formulas, single cell styling, multiple styles in a single cell
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link WriteCellDemoData}
- *
- * 2. Write directly
- */
- @Test
- public void writeCellDataWrite() {
- String fileName = TestFileUtil.getPath() + "writeCellDataWrite" + System.currentTimeMillis() + ".xlsx";
- WriteCellDemoData writeCellDemoData = new WriteCellDemoData();
-
- // Set hyperlink
- WriteCellData hyperlink = new WriteCellData<>("Official Website");
- writeCellDemoData.setHyperlink(hyperlink);
- HyperlinkData hyperlinkData = new HyperlinkData();
- hyperlink.setHyperlinkData(hyperlinkData);
- hyperlinkData.setAddress("https://github.com/fast-excel/fastexcel");
- hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);
-
- // Set comment
- WriteCellData comment = new WriteCellData<>("Comment cell info");
- writeCellDemoData.setCommentData(comment);
- CommentData commentData = new CommentData();
- comment.setCommentData(commentData);
- commentData.setAuthor("Jiaju Zhuang");
- commentData.setRichTextStringData(new RichTextStringData("This is a comment"));
- // The default size of the comment is the size of the cell. Here we want to adjust it to the size of 4 cells, so
- // we occupy one extra cell to the right and one extra cell down.
- commentData.setRelativeLastColumnIndex(1);
- commentData.setRelativeLastRowIndex(1);
-
- // Set formula
- WriteCellData formula = new WriteCellData<>();
- writeCellDemoData.setFormulaData(formula);
- FormulaData formulaData = new FormulaData();
- formula.setFormulaData(formulaData);
- // Replace the first digit in 123456789 with 2
- // This is just an example. If it involves formulas, try to calculate them in memory if possible. Avoid using
- // formulas if possible.
- formulaData.setFormulaValue("REPLACE(123456789,1,1,2)");
-
- // Set style for a single cell. If there are many styles, you can use annotations.
- WriteCellData writeCellStyle = new WriteCellData<>("Cell Style");
- writeCellStyle.setType(CellDataTypeEnum.STRING);
- writeCellDemoData.setWriteCellStyle(writeCellStyle);
- WriteCellStyle writeCellStyleData = new WriteCellStyle();
- writeCellStyle.setWriteCellStyle(writeCellStyleData);
- // Need to specify FillPatternType as FillPatternType.SOLID_FOREGROUND, otherwise background color will not be
- // displayed.
- writeCellStyleData.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
- // Green background
- writeCellStyleData.setFillForegroundColor(IndexedColors.GREEN.getIndex());
-
- // Set multiple styles in a single cell
- // Need to set inMemory=true, otherwise multiple styles in a single cell cannot be displayed. Use with caution.
- WriteCellData richTest = new WriteCellData<>();
- richTest.setType(CellDataTypeEnum.RICH_TEXT_STRING);
- writeCellDemoData.setRichText(richTest);
- RichTextStringData richTextStringData = new RichTextStringData();
- richTest.setRichTextStringDataValue(richTextStringData);
- richTextStringData.setTextString("Red Green Default");
- // First 3 characters are red
- WriteFont writeFont = new WriteFont();
- writeFont.setColor(IndexedColors.RED.getIndex());
- richTextStringData.applyFont(0, 3, writeFont);
- // Next 5 characters are green
- writeFont = new WriteFont();
- writeFont.setColor(IndexedColors.GREEN.getIndex());
- richTextStringData.applyFont(4, 9, writeFont);
-
- List data = new ArrayList<>();
- data.add(writeCellDemoData);
- FesodSheet.write(fileName, WriteCellDemoData.class)
- .inMemory(true)
- .sheet("Template")
- .doWrite(data);
- }
-
- /**
- * Write according to template
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link IndexData}
- *
- * 2. Use {@link ExcelProperty} annotation to specify columns to write
- *
- * 3. Use withTemplate to read template
- *
- * 4. Write directly
- */
- @Test
- public void templateWrite() {
- String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
- String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- // Note: withTemplate will store the entire template file in memory, so try not to use it for appending files.
- // If the template file is too large, it will cause OOM.
- // If you want to append to a file (cannot be processed in one thread, refer to the repeated writing demo
- // recommended for one thread), it is recommended to store temporarily in the database or disk cache (ehcache)
- // and then write all at once.
- FesodSheet.write(fileName, DemoData.class)
- .withTemplate(templateFileName)
- .sheet()
- .doWrite(data());
- }
-
- /**
- * Column width and row height
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link WidthAndHeightData }
- *
- * 2. Use annotations {@link ColumnWidth}, {@link HeadRowHeight}, {@link ContentRowHeight} to specify width or height
- *
- * 3. Write directly
- */
- @Test
- public void widthAndHeightWrite() {
- String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, WidthAndHeightData.class).sheet("Template").doWrite(data());
- }
-
- /**
- * Custom style via annotations
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoStyleData}
- *
- * 3. Write directly
- */
- @Test
- public void annotationStyleWrite() {
- String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoStyleData.class).sheet("Template").doWrite(data());
- }
-
- /**
- * Custom style via handlers
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Create a style strategy and register it
- *
- * 3. Write directly
- */
- @Test
- public void handlerStyleWrite() {
- // Method 1: Use existing strategies (Recommended)
- // HorizontalCellStyleStrategy: styles are the same for each row or alternating rows
- // AbstractVerticalCellStyleStrategy: styles are the same for each column. Need to subclass and implement.
- String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx";
- // Head strategy
- WriteCellStyle headWriteCellStyle = new WriteCellStyle();
- // Background red
- headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
- WriteFont headWriteFont = new WriteFont();
- headWriteFont.setFontHeightInPoints((short) 20);
- headWriteCellStyle.setWriteFont(headWriteFont);
- // Content strategy
- WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
- // Need to specify FillPatternType as SOLID_FOREGROUND. The head defaults to FillPatternType so it can be
- // omitted.
- contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
- // Background green
- contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
- WriteFont contentWriteFont = new WriteFont();
- // Font size
- contentWriteFont.setFontHeightInPoints((short) 20);
- contentWriteCellStyle.setWriteFont(contentWriteFont);
- // This strategy separates head style and content style. You can implement other strategies yourself.
- HorizontalCellStyleStrategy horizontalCellStyleStrategy =
- new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
-
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoData.class)
- .registerWriteHandler(horizontalCellStyleStrategy)
- .sheet("Template")
- .doWrite(data());
-
- // Method 2: Write your own handler using Fesod API. Not recommended. Try to use existing strategies.
- fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx";
- FesodSheet.write(fileName, DemoData.class)
- .registerWriteHandler(new CellWriteHandler() {
- @Override
- public void afterCellDispose(CellWriteHandlerContext context) {
- // This event is called after data is set into the POI cell
- // Check if it's not a head. If it's fill, this will be null, so use not true.
- if (BooleanUtils.isNotTrue(context.getHead())) {
- // First cell
- // As long as it's not a head, there will be data. Of course in fill scenarios, use
- // context.getCellDataList(). Depending on the template, a cell may have multiple
- // WriteCellData.
- WriteCellData> cellData = context.getFirstCellData();
- // Need to get style from cellData
- // A very important reason is that WriteCellStyle is bound to dataFormatData. For example,
- // if you add DateTimeFormat,
- // the dataFormatData in writeCellStyle has been changed. If you new a WriteCellStyle
- // yourself, the annotation style may be lost.
- // getOrCreateStyle returns a style, creating one if it's null.
- WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
- writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
- // Need to specify FillPatternType as SOLID_FOREGROUND
- writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
-
- // The style is set. There is a FillStyleCellWriteHandler later that will default set
- // WriteCellStyle to the cell, so you don't need to worry about it.
- }
- }
- })
- .sheet("Template")
- .doWrite(data());
-
- // Method 3: Use POI styles directly. Not recommended.
- // Pitfall 1: Style contains dataformat for formatting data, so setting it yourself may cause formatting
- // annotations to fail.
- // Pitfall 2: Don't keep creating styles. Remember to cache them. Creating more than 60,000 will crash.
- fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx";
- FesodSheet.write(fileName, DemoData.class)
- .registerWriteHandler(new CellWriteHandler() {
- @Override
- public void afterCellDispose(CellWriteHandlerContext context) {
- // This event is called after data is set into the POI cell
- // Check if it's not a head. If it's fill, this will be null, so use not true.
- if (BooleanUtils.isNotTrue(context.getHead())) {
- Cell cell = context.getCell();
- // Get POI workbook
- Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
- // Remember to cache reusable parts. A table can have at most 60,000 styles.
- // Try to pass the same cellStyle for different cells
- CellStyle cellStyle = workbook.createCellStyle();
- cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
- // Need to specify FillPatternType as SOLID_FOREGROUND
- cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
- cell.setCellStyle(cellStyle);
-
- // Since dataformat is not specified here, the displayed data format may be incorrect.
-
- // Clear the style of WriteCellData. Otherwise, FillStyleCellWriteHandler will override your
- // settings.
- context.getFirstCellData().setWriteCellStyle(null);
- }
- }
- })
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Merge cells
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData} {@link DemoMergeData}
- *
- * 2. Create a merge strategy and register it
- *
- * 3. Write directly
- */
- @Test
- public void mergeWrite() {
- // Method 1: Annotation
- String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";
- // Add ContentLoopMerge annotation in DemoStyleData
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoMergeData.class).sheet("Template").doWrite(data());
-
- // Method 2: Custom merge strategy
- fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";
- // Merge every 2 rows. Set eachColumn to 3 (length of our data), so only the first column will merge. Other
- // merge strategies can be implemented.
- LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoData.class)
- .registerWriteHandler(loopMergeStrategy)
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Write using table
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Then write to the table
- */
- @Test
- public void tableWrite() {
- String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx";
- // Method 1: Writing multiple tables here. If there is only one, it can be done in one line.
- // Specify the class to use for writing
- try (ExcelWriter excelWriter =
- FesodSheet.write(fileName, DemoData.class).build()) {
- // Set sheet to not need head, otherwise it will output sheet head, looking like the first table has 2
- // heads.
- WriteSheet writeSheet =
- FesodSheet.writerSheet("Template").needHead(Boolean.FALSE).build();
- // Must specify need head here. Table inherits sheet configuration. If sheet is configured not to need it,
- // table defaults to not needing it.
- WriteTable writeTable0 =
- FesodSheet.writerTable(0).needHead(Boolean.TRUE).build();
- WriteTable writeTable1 =
- FesodSheet.writerTable(1).needHead(Boolean.TRUE).build();
- // First write will create head
- excelWriter.write(data(), writeSheet, writeTable0);
- // Second write will also create head, writing data after the first one.
- excelWriter.write(data(), writeSheet, writeTable1);
- }
- }
-
- /**
- * Dynamic header writing
- *
- * The idea is to first create a sheet with List head format, writing only the head, then write data via table without writing head.
- *
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Then write to the table
- */
- @Test
- public void dynamicHeadWrite() {
- String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";
- FesodSheet.write(fileName)
- // Put dynamic head here
- .head(head())
- .sheet("Template")
- // Of course data can also be passed as List>
- .doWrite(data());
- }
-
- /**
- * Auto column width (not very precise)
- *
- * This is not very easy to use currently. For example, numbers will cause line breaks. And the length is not exactly consistent with actual length. Use with caution if precise column width is needed. You can also re-implement referencing {@link LongestMatchColumnWidthStyleStrategy}.
- *
- * POI's built-in {@link SXSSFSheet#autoSizeColumn(int)} also doesn't support Chinese very well. No good algorithm found yet.
- *
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link LongestMatchColumnWidthData}
- *
- * 3. Write directly
- */
- @Test
- public void longestMatchColumnWidthWrite() {
- String fileName =
- TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, LongestMatchColumnWidthData.class)
- .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
- .sheet("Template")
- .doWrite(dataLong());
- }
-
- /**
- * Custom handlers for dropdowns, hyperlinks, etc. (Refer to this for operations that don't fit above points but need to manipulate cells)
- *
- * Demo implements 2 points: 1. Hyperlink the head of the first row and first column to URL. 2. Add dropdown box for data in the first column, first and second rows, displaying "Test1", "Test2".
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Write directly
- */
- @Test
- public void customHandlerWrite() {
- String fileName = TestFileUtil.getPath() + "customHandlerWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, DemoData.class)
- .registerWriteHandler(new CustomSheetWriteHandler())
- .registerWriteHandler(new CustomCellWriteHandler())
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Insert comment
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link DemoData}
- *
- * 2. Write directly
- */
- @Test
- public void commentWrite() {
- String fileName = TestFileUtil.getPath() + "commentWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- // Note that inMemory must be set to true to support comments. Currently there is no good way to handle comments
- // without being in memory.
- FesodSheet.write(fileName, DemoData.class)
- .inMemory(Boolean.TRUE)
- .registerWriteHandler(new CommentWriteHandler())
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Variable title handling (including title internationalization, etc.)
- *
- * Simply put, use List> for titles but still support annotations
- *
- * 1. Create the entity object corresponding to Excel. Refer to {@link ConverterData}
- *
- * 2. Write directly
- */
- @Test
- public void variableTitleWrite() {
- // Method 1
- String fileName = TestFileUtil.getPath() + "variableTitleWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName, ConverterData.class)
- .head(variableTitleHead())
- .sheet("Template")
- .doWrite(data());
- }
-
- /**
- * Write without creating objects
- */
- @Test
- public void noModelWrite() {
- // Method 1
- String fileName = TestFileUtil.getPath() + "noModelWrite" + System.currentTimeMillis() + ".xlsx";
- // Specify the class to use for writing, then write to the first sheet with the name "Template". The file stream
- // will be automatically closed.
- FesodSheet.write(fileName).head(head()).sheet("Template").doWrite(dataList());
- }
-
- @Test
- public void sheetDisposeTest() {
- String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
- FesodSheet.write(fileName, DemoData.class)
- .sheet("Template")
- .registerWriteHandler(new SheetWriteHandler() {
- @Override
- public void afterSheetDispose(SheetWriteHandlerContext context) {
- Sheet sheet = context.getWriteSheetHolder().getSheet();
- // Merge region cells
- sheet.addMergedRegionUnsafe(new CellRangeAddress(1, 10, 2, 2));
- }
- })
- .doWrite(this::data);
- System.out.println(fileName);
- }
-
- private List dataLong() {
- List list = ListUtils.newArrayList();
- for (int i = 0; i < 10; i++) {
- LongestMatchColumnWidthData data = new LongestMatchColumnWidthData();
- data.setString("Testing very long string Testing very long string Testing very long string" + i);
- data.setDate(new Date());
- data.setDoubleData(1000000000000.0);
- list.add(data);
- }
- return list;
- }
-
- private List> variableTitleHead() {
- List> list = ListUtils.newArrayList();
- List head0 = ListUtils.newArrayList();
- head0.add("string" + System.currentTimeMillis());
- List head1 = ListUtils.newArrayList();
- head1.add("number" + System.currentTimeMillis());
- List head2 = ListUtils.newArrayList();
- head2.add("date" + System.currentTimeMillis());
- list.add(head0);
- list.add(head1);
- list.add(head2);
- return list;
- }
-
- private List> head() {
- List> list = ListUtils.newArrayList();
- List head0 = ListUtils.newArrayList();
- head0.add("String" + System.currentTimeMillis());
- List head1 = ListUtils.newArrayList();
- head1.add("Double" + System.currentTimeMillis());
- List head2 = ListUtils.newArrayList();
- head2.add("Date" + System.currentTimeMillis());
- list.add(head0);
- list.add(head1);
- list.add(head2);
- return list;
- }
-
- private List> dataList() {
- List> list = ListUtils.newArrayList();
- for (int i = 0; i < 10; i++) {
- List