This is an automated email from the ASF dual-hosted git repository.
psxjoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fesod.git
The following commit(s) were added to refs/heads/main by this push:
new 49790895 feat: support freeze pane via FreezePane or register
WriteHandler on write (#916)
49790895 is described below
commit 497908955a979820f5813f31548e4ca7cd9a5cb4
Author: Bengbengbalabalabeng
<[email protected]>
AuthorDate: Wed Jun 10 21:14:24 2026 +0800
feat: support freeze pane via FreezePane or register WriteHandler on write
(#916)
* feat: support freeze pane via @FreezePane or register WriteHandler on
write
* refactor(code-style): refactor part of the code implementation
- add default value for annotation fields
- delete redundant references
* feat: add overload constructor for SheetFreezePaneStrategy
* docs: add @FreezePane annotation usage documentation
* refactor: modify test method name
* docs: update the usage documentation for the @FreezePane annotation
---
.../sheet/annotation/write/style/FreezePane.java | 55 ++++
.../metadata/property/SheetFreezePaneProperty.java | 76 ++++++
.../write/metadata/holder/AbstractWriteHolder.java | 11 +
.../write/property/ExcelWriteHeadProperty.java | 4 +
.../sheet/write/style/SheetFreezePaneStrategy.java | 77 ++++++
.../property/SheetFreezePanePropertyTest.java | 87 +++++++
.../write/freezepane/SheetFreezePaneTest.java | 279 +++++++++++++++++++++
.../write/style/SheetFreezePaneStrategyTest.java | 116 +++++++++
website/docs/sheet/help/annotation.md | 11 +
.../current/sheet/help/annotation.md | 11 +
10 files changed, 727 insertions(+)
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/annotation/write/style/FreezePane.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/annotation/write/style/FreezePane.java
new file mode 100644
index 00000000..225f8d56
--- /dev/null
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/annotation/write/style/FreezePane.java
@@ -0,0 +1,55 @@
+/*
+ * 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.annotation.write.style;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to define a freeze pane for an Excel sheet.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface FreezePane {
+
+ /**
+ * Horizontal position of split.
+ */
+ int colSplit() default 0;
+
+ /**
+ * Vertical position of split.
+ */
+ int rowSplit() default 0;
+
+ /**
+ * Left column visible in right pane. By default, it's equal to colSplit.
+ */
+ int leftmostColumn() default -1;
+
+ /**
+ * Top row visible in bottom pane. By default, it's equal to rowSplit.
+ */
+ int topRow() default -1;
+}
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/property/SheetFreezePaneProperty.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/property/SheetFreezePaneProperty.java
new file mode 100644
index 00000000..4d8490fb
--- /dev/null
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/property/SheetFreezePaneProperty.java
@@ -0,0 +1,76 @@
+/*
+ * 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.metadata.property;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fesod.sheet.annotation.write.style.FreezePane;
+
+/**
+ * Property configuration for an Excel sheet freeze pane.
+ * <p>
+ * This class holds the configuration details required to create a freeze pane.
+ * It is typically built from the {@link FreezePane} annotation.
+ */
+@Getter
+@Setter
+@EqualsAndHashCode
+public class SheetFreezePaneProperty {
+
+ /**
+ * Horizontal position of split.
+ */
+ private int colSplit;
+
+ /**
+ * Vertical position of split.
+ */
+ private int rowSplit;
+
+ /**
+ * Left column visible in right pane.
+ */
+ private int leftmostColumn;
+
+ /**
+ * Top row visible in bottom pane
+ */
+ private int topRow;
+
+ public static SheetFreezePaneProperty build(FreezePane freezePane) {
+ if (freezePane == null) {
+ return null;
+ }
+ SheetFreezePaneProperty result = new SheetFreezePaneProperty();
+ result.setColSplit(freezePane.colSplit());
+ result.setRowSplit(freezePane.rowSplit());
+ result.setLeftmostColumn(getOrDefault(freezePane.leftmostColumn(),
freezePane.colSplit()));
+ result.setTopRow(getOrDefault(freezePane.topRow(),
freezePane.rowSplit()));
+ return result;
+ }
+
+ private static Integer getOrDefault(Integer value, Integer defaultValue) {
+ if (value == -1) {
+ return defaultValue;
+ }
+ return value;
+ }
+}
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java
index be35c494..293f18ad 100644
---
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java
@@ -51,6 +51,7 @@ import
org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
import org.apache.fesod.sheet.metadata.property.LoopMergeProperty;
import org.apache.fesod.sheet.metadata.property.OnceAbsoluteMergeProperty;
import org.apache.fesod.sheet.metadata.property.RowHeightProperty;
+import org.apache.fesod.sheet.metadata.property.SheetFreezePaneProperty;
import org.apache.fesod.sheet.write.handler.CellWriteHandler;
import org.apache.fesod.sheet.write.handler.DefaultWriteHandlerLoader;
import org.apache.fesod.sheet.write.handler.RowWriteHandler;
@@ -68,6 +69,7 @@ import
org.apache.fesod.sheet.write.metadata.WriteBasicParameter;
import org.apache.fesod.sheet.write.metadata.style.WriteCellStyle;
import org.apache.fesod.sheet.write.property.ExcelWriteHeadProperty;
import org.apache.fesod.sheet.write.style.AbstractVerticalCellStyleStrategy;
+import org.apache.fesod.sheet.write.style.SheetFreezePaneStrategy;
import
org.apache.fesod.sheet.write.style.column.AbstractHeadColumnWidthStyleStrategy;
import org.apache.fesod.sheet.write.style.row.SimpleRowHeightStyleStrategy;
@@ -342,6 +344,7 @@ public abstract class AbstractWriteHolder extends
AbstractHolder implements Writ
dealStyle(handlerList);
dealRowHigh(handlerList);
dealOnceAbsoluteMerge(handlerList);
+ dealSheetFreezePane(handlerList);
}
private void dealStyle(List<WriteHandler> handlerList) {
@@ -387,6 +390,14 @@ public abstract class AbstractWriteHolder extends
AbstractHolder implements Writ
handlerList.add(new
OnceAbsoluteMergeStrategy(onceAbsoluteMergeProperty));
}
+ private void dealSheetFreezePane(List<WriteHandler> handlerList) {
+ SheetFreezePaneProperty freezePaneProperty =
getExcelWriteHeadProperty().getFreezePaneProperty();
+ if (freezePaneProperty == null) {
+ return;
+ }
+ handlerList.add(new SheetFreezePaneStrategy(freezePaneProperty));
+ }
+
private void dealRowHigh(List<WriteHandler> handlerList) {
RowHeightProperty headRowHeightProperty =
getExcelWriteHeadProperty().getHeadRowHeightProperty();
RowHeightProperty contentRowHeightProperty =
getExcelWriteHeadProperty().getContentRowHeightProperty();
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/property/ExcelWriteHeadProperty.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/property/ExcelWriteHeadProperty.java
index 92cbbea2..fb97d78c 100644
---
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/property/ExcelWriteHeadProperty.java
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/property/ExcelWriteHeadProperty.java
@@ -38,6 +38,7 @@ import lombok.Setter;
import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
import org.apache.fesod.sheet.annotation.write.style.ContentLoopMerge;
import org.apache.fesod.sheet.annotation.write.style.ContentRowHeight;
+import org.apache.fesod.sheet.annotation.write.style.FreezePane;
import org.apache.fesod.sheet.annotation.write.style.HeadFontStyle;
import org.apache.fesod.sheet.annotation.write.style.HeadRowHeight;
import org.apache.fesod.sheet.annotation.write.style.HeadStyle;
@@ -53,6 +54,7 @@ import org.apache.fesod.sheet.metadata.property.FontProperty;
import org.apache.fesod.sheet.metadata.property.LoopMergeProperty;
import org.apache.fesod.sheet.metadata.property.OnceAbsoluteMergeProperty;
import org.apache.fesod.sheet.metadata.property.RowHeightProperty;
+import org.apache.fesod.sheet.metadata.property.SheetFreezePaneProperty;
import org.apache.fesod.sheet.metadata.property.StyleProperty;
/**
@@ -67,6 +69,7 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty
{
private RowHeightProperty headRowHeightProperty;
private RowHeightProperty contentRowHeightProperty;
private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty;
+ private SheetFreezePaneProperty freezePaneProperty;
public ExcelWriteHeadProperty(
ConfigurationHolder configurationHolder, Class<?> headClazz,
List<List<String>> head) {
@@ -78,6 +81,7 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty
{
this.contentRowHeightProperty =
RowHeightProperty.build(headClazz.getAnnotation(ContentRowHeight.class));
this.onceAbsoluteMergeProperty =
OnceAbsoluteMergeProperty.build(headClazz.getAnnotation(OnceAbsoluteMerge.class));
+ this.freezePaneProperty =
SheetFreezePaneProperty.build(headClazz.getAnnotation(FreezePane.class));
ColumnWidth parentColumnWidth =
headClazz.getAnnotation(ColumnWidth.class);
HeadStyle parentHeadStyle = headClazz.getAnnotation(HeadStyle.class);
diff --git
a/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategy.java
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategy.java
new file mode 100644
index 00000000..f54b1c66
--- /dev/null
+++
b/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * 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.write.style;
+
+import org.apache.fesod.common.util.ValidateUtils;
+import org.apache.fesod.sheet.metadata.property.SheetFreezePaneProperty;
+import org.apache.fesod.sheet.write.handler.SheetWriteHandler;
+import org.apache.fesod.sheet.write.metadata.holder.WriteSheetHolder;
+import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder;
+
+/**
+ * A strategy for creating a freeze pane. Any existing freeze pane or split
pane is overwritten.
+ */
+public class SheetFreezePaneStrategy implements SheetWriteHandler {
+
+ /**
+ * Horizontal position of split.
+ */
+ private final int colSplit;
+
+ /**
+ * Vertical position of split.
+ */
+ private final int rowSplit;
+
+ /**
+ * Left column visible in right pane.
+ */
+ private final int leftmostColumn;
+
+ /**
+ * Top row visible in bottom pane
+ */
+ private final int topRow;
+
+ public SheetFreezePaneStrategy(SheetFreezePaneProperty property) {
+ this(property.getColSplit(), property.getRowSplit(),
property.getLeftmostColumn(), property.getTopRow());
+ }
+
+ public SheetFreezePaneStrategy(int colSplit, int rowSplit) {
+ this(colSplit, rowSplit, colSplit, rowSplit);
+ }
+
+ public SheetFreezePaneStrategy(int colSplit, int rowSplit, int
leftmostColumn, int topRow) {
+ ValidateUtils.isTrue(colSplit >= 0, "colSplit must be >= 0");
+ ValidateUtils.isTrue(rowSplit >= 0, "rowSplit must be >= 0");
+ ValidateUtils.isTrue(leftmostColumn >= 0, "leftmostColumn must be >=
0");
+ ValidateUtils.isTrue(topRow >= 0, "topRow must be >= 0");
+
+ this.colSplit = colSplit;
+ this.rowSplit = rowSplit;
+ this.leftmostColumn = leftmostColumn;
+ this.topRow = topRow;
+ }
+
+ @Override
+ public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
WriteSheetHolder writeSheetHolder) {
+ writeSheetHolder.getSheet().createFreezePane(colSplit, rowSplit,
leftmostColumn, topRow);
+ }
+}
diff --git
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/metadata/property/SheetFreezePanePropertyTest.java
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/metadata/property/SheetFreezePanePropertyTest.java
new file mode 100644
index 00000000..3d13fda2
--- /dev/null
+++
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/metadata/property/SheetFreezePanePropertyTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.metadata.property;
+
+import org.apache.fesod.sheet.annotation.write.style.FreezePane;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class SheetFreezePanePropertyTest {
+
+ @Test
+ void build_returnsNull_whenAnnotationIsNull() {
+ Assertions.assertNull(SheetFreezePaneProperty.build(null));
+ }
+
+ @Test
+ void build_mapsColSplitAndRowSplit() {
+ FreezePane annotation =
FreezePaneClass.class.getAnnotation(FreezePane.class);
+ SheetFreezePaneProperty property =
SheetFreezePaneProperty.build(annotation);
+
+ Assertions.assertNotNull(property);
+ Assertions.assertEquals(2, property.getColSplit());
+ Assertions.assertEquals(3, property.getRowSplit());
+ }
+
+ @Test
+ void build_usesDefaults_whenLeftmostColumnAndTopRowNotSet() {
+ FreezePane annotation =
DefaultsClass.class.getAnnotation(FreezePane.class);
+ SheetFreezePaneProperty property =
SheetFreezePaneProperty.build(annotation);
+
+ Assertions.assertNotNull(property);
+ Assertions.assertEquals(4, property.getLeftmostColumn());
+ Assertions.assertEquals(5, property.getTopRow());
+ }
+
+ @Test
+ void build_usesExplicitValues_whenLeftmostColumnAndTopRowSet() {
+ FreezePane annotation =
ExplicitValuesClass.class.getAnnotation(FreezePane.class);
+ SheetFreezePaneProperty property =
SheetFreezePaneProperty.build(annotation);
+
+ Assertions.assertNotNull(property);
+ Assertions.assertEquals(10, property.getLeftmostColumn());
+ Assertions.assertEquals(20, property.getTopRow());
+ }
+
+ @Test
+ void build_zeroColSplitAndRowSplit() {
+ FreezePane annotation =
ZeroSplitClass.class.getAnnotation(FreezePane.class);
+ SheetFreezePaneProperty property =
SheetFreezePaneProperty.build(annotation);
+
+ Assertions.assertNotNull(property);
+ Assertions.assertEquals(0, property.getColSplit());
+ Assertions.assertEquals(0, property.getRowSplit());
+ // When not specified, defaults should fall back to 0 (the split
values)
+ Assertions.assertEquals(0, property.getLeftmostColumn());
+ Assertions.assertEquals(0, property.getTopRow());
+ }
+
+ @FreezePane(colSplit = 2, rowSplit = 3)
+ static class FreezePaneClass {}
+
+ @FreezePane(colSplit = 4, rowSplit = 5)
+ static class DefaultsClass {}
+
+ @FreezePane(colSplit = 1, rowSplit = 1, leftmostColumn = 10, topRow = 20)
+ static class ExplicitValuesClass {}
+
+ @FreezePane(colSplit = 0, rowSplit = 0)
+ static class ZeroSplitClass {}
+}
diff --git
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/freezepane/SheetFreezePaneTest.java
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/freezepane/SheetFreezePaneTest.java
new file mode 100644
index 00000000..c1695e15
--- /dev/null
+++
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/freezepane/SheetFreezePaneTest.java
@@ -0,0 +1,279 @@
+/*
+ * 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.write.freezepane;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+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.write.style.FreezePane;
+import org.apache.fesod.sheet.write.style.SheetFreezePaneStrategy;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.PaneInformation;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class SheetFreezePaneTest {
+
+ private File file07;
+ private File file03;
+
+ @BeforeEach
+ void setup(@TempDir Path dir) {
+ this.file07 = createTmpFile(dir, "writeFreezepane07.xlsx");
+ this.file03 = createTmpFile(dir, "writeFreezepane03.xls");
+ }
+
+ private File createTmpFile(Path dir, String filename) {
+ return new File(dir.resolve(filename).toString());
+ }
+
+ interface FreezePaneModel {
+
+ void setStr1(String str1);
+
+ String getStr1();
+
+ void setStr2(String str2);
+
+ String getStr2();
+ }
+
+ @Getter
+ @Setter
+ @FreezePane(colSplit = 2, rowSplit = 1)
+ static class FreezeTwoColsOneRowData implements FreezePaneModel {
+ @ExcelProperty("Str 1")
+ private String str1;
+
+ @ExcelProperty("Str 2")
+ private String str2;
+ }
+
+ @Getter
+ @Setter
+ @FreezePane(rowSplit = 1)
+ static class FreezeFirstRowOnlyData implements FreezePaneModel {
+ @ExcelProperty("Str 1")
+ private String str1;
+
+ @ExcelProperty("Str 2")
+ private String str2;
+ }
+
+ @Getter
+ @Setter
+ @FreezePane(colSplit = 1)
+ static class FreezeFirstColOnlyData implements FreezePaneModel {
+ @ExcelProperty("Str 1")
+ private String str1;
+
+ @ExcelProperty("Str 2")
+ private String str2;
+ }
+
+ @Getter
+ @Setter
+ @FreezePane(colSplit = 2, rowSplit = 3, leftmostColumn = 5, topRow = 10)
+ static class FreezePaneData implements FreezePaneModel {
+ @ExcelProperty("Str 1")
+ private String str1;
+
+ @ExcelProperty("Str 2")
+ private String str2;
+ }
+
+ @Getter
+ @Setter
+ static class NoFreezePaneData implements FreezePaneModel {
+ @ExcelProperty("Str 1")
+ private String str1;
+
+ @ExcelProperty("Str 2")
+ private String str2;
+ }
+
+ private <T extends FreezePaneModel> List<FreezePaneModel>
buildData(Class<T> clazz, int rows) {
+ try {
+ List<FreezePaneModel> list = new ArrayList<>();
+ for (int i = 0; i < rows; i++) {
+ FreezePaneModel obj =
clazz.getDeclaredConstructor().newInstance();
+ obj.setStr1("String1-" + i);
+ obj.setStr2("String2-" + i);
+ list.add(obj);
+ }
+ return list;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void assertFreezePane(
+ File file, int expectedColSplit, int expectedRowSplit, int
expectedLeftmost, int expectedTopRow)
+ throws Exception {
+ try (Workbook workbook = WorkbookFactory.create(file)) {
+ Sheet sheet = workbook.getSheetAt(0);
+ PaneInformation pane = sheet.getPaneInformation();
+ if (expectedColSplit == 0 && expectedRowSplit == 0) {
+ Assertions.assertNull(pane, "Expected no freeze pane");
+ return;
+ }
+ Assertions.assertNotNull(pane, "Expected freeze pane to be set");
+ Assertions.assertEquals(
+ expectedColSplit, pane.getVerticalSplitPosition(),
"Vertical split (colSplit) mismatch");
+ Assertions.assertEquals(
+ expectedRowSplit, pane.getHorizontalSplitPosition(),
"Horizontal split (rowSplit) mismatch");
+ Assertions.assertEquals(expectedLeftmost,
pane.getVerticalSplitLeftColumn(), "Leftmost column mismatch");
+ Assertions.assertEquals(expectedTopRow,
pane.getHorizontalSplitTopRow(), "Top row mismatch");
+ }
+ }
+
+ @Test
+ void should_freezeTwoColsOneRow_whenAnnotationColSplit2RowSplit1() throws
Exception {
+ runFreezeTwoColsOneRow(file07);
+ runFreezeTwoColsOneRow(file03);
+ }
+
+ private void runFreezeTwoColsOneRow(File file) throws Exception {
+ FesodSheet.write(file, FreezeTwoColsOneRowData.class)
+ .sheet()
+ .doWrite(buildData(FreezeTwoColsOneRowData.class, 5));
+ assertFreezePane(file, 2, 1, 2, 1);
+ }
+
+ @Test
+ void should_freezeHeaderRowOnly_whenAnnotationColSplit0RowSplit1() throws
Exception {
+ runFreezeHeaderRowOnly(file07);
+ runFreezeHeaderRowOnly(file03);
+ }
+
+ private void runFreezeHeaderRowOnly(File file) throws Exception {
+ FesodSheet.write(file, FreezeFirstRowOnlyData.class)
+ .sheet()
+ .doWrite(buildData(FreezeFirstRowOnlyData.class, 5));
+ assertFreezePane(file, 0, 1, 0, 1);
+ }
+
+ @Test
+ void should_freezeFirstColOnly_whenAnnotationColSplit1RowSplit0() throws
Exception {
+ runFreezeFirstColOnly(file07);
+ runFreezeFirstColOnly(file03);
+ }
+
+ private void runFreezeFirstColOnly(File file) throws Exception {
+ FesodSheet.write(file, FreezeFirstColOnlyData.class)
+ .sheet()
+ .doWrite(buildData(FreezeFirstColOnlyData.class, 5));
+ assertFreezePane(file, 1, 0, 1, 0);
+ }
+
+ @Test
+ void should_useExplicitPanePositions_whenAnnotationSetsLeftmostAndTopRow()
throws Exception {
+ runUseExplicitPanePositions(file07);
+ runUseExplicitPanePositions(file03);
+ }
+
+ private void runUseExplicitPanePositions(File file) throws Exception {
+ FesodSheet.write(file,
FreezePaneData.class).sheet().doWrite(buildData(FreezePaneData.class, 5));
+ assertFreezePane(file, 2, 3, 5, 10);
+ }
+
+ @Test
+ void should_notSetFreezePane_whenAnnotationAbsent() throws Exception {
+ runNotSetFreezePane(file07);
+ runNotSetFreezePane(file03);
+ }
+
+ private void runNotSetFreezePane(File file) throws Exception {
+ FesodSheet.write(file,
NoFreezePaneData.class).sheet().doWrite(buildData(NoFreezePaneData.class, 5));
+ try (Workbook workbook = WorkbookFactory.create(file)) {
+ Sheet sheet = workbook.getSheetAt(0);
+ Assertions.assertNull(sheet.getPaneInformation());
+ }
+ }
+
+ @Test
+ void should_applyFreezePane_whenRegisteredViaWriteHandler() throws
Exception {
+ runApplyFreezePaneStrategy(file07, false);
+ runApplyFreezePaneStrategy(file03, false);
+ runApplyFreezePaneStrategy(file07, true);
+ runApplyFreezePaneStrategy(file03, true);
+ }
+
+ private void runApplyFreezePaneStrategy(File file, boolean useSimplify)
throws Exception {
+ SheetFreezePaneStrategy strategy;
+ if (useSimplify) {
+ strategy = new SheetFreezePaneStrategy(1, 1);
+ } else {
+ strategy = new SheetFreezePaneStrategy(1, 1, 1, 1);
+ }
+
+ FesodSheet.write(file, NoFreezePaneData.class)
+ .registerWriteHandler(strategy)
+ .sheet()
+ .doWrite(buildData(NoFreezePaneData.class, 5));
+ assertFreezePane(file, 1, 1, 1, 1);
+ }
+
+ @Test
+ void should_applyDifferentFreezePanes_whenMultipleSheets() throws
Exception {
+ runApplyDifferentFreezePanes(file07);
+ runApplyDifferentFreezePanes(file03);
+ }
+
+ private void runApplyDifferentFreezePanes(File file) throws Exception {
+ try (ExcelWriter excelWriter = FesodSheet.write(file).build()) {
+ excelWriter.write(
+ buildData(FreezeTwoColsOneRowData.class, 3),
+ FesodSheet.writerSheet(0)
+ .head(FreezeTwoColsOneRowData.class)
+ .build());
+
+ excelWriter.write(
+ buildData(FreezeFirstRowOnlyData.class, 3),
+
FesodSheet.writerSheet(1).head(FreezeFirstRowOnlyData.class).build());
+ }
+
+ try (Workbook workbook = WorkbookFactory.create(file)) {
+ // Sheet 0: colSplit=2, rowSplit=1
+ Sheet sheet0 = workbook.getSheetAt(0);
+ PaneInformation pane0 = sheet0.getPaneInformation();
+ Assertions.assertNotNull(pane0);
+ Assertions.assertEquals(2, pane0.getVerticalSplitPosition());
+ Assertions.assertEquals(1, pane0.getHorizontalSplitPosition());
+
+ // Sheet 1: colSplit=0, rowSplit=1
+ Sheet sheet1 = workbook.getSheetAt(1);
+ PaneInformation pane1 = sheet1.getPaneInformation();
+ Assertions.assertNotNull(pane1);
+ Assertions.assertEquals(0, pane1.getVerticalSplitPosition());
+ Assertions.assertEquals(1, pane1.getHorizontalSplitPosition());
+ }
+ }
+}
diff --git
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategyTest.java
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategyTest.java
new file mode 100644
index 00000000..f89996ca
--- /dev/null
+++
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/write/style/SheetFreezePaneStrategyTest.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.write.style;
+
+import org.apache.fesod.sheet.metadata.property.SheetFreezePaneProperty;
+import org.apache.fesod.sheet.write.metadata.holder.WriteSheetHolder;
+import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class SheetFreezePaneStrategyTest {
+
+ @Test
+ void constructor_throws_whenColSplitNegative() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
SheetFreezePaneStrategy(-1, 0, 0, 0));
+ }
+
+ @Test
+ void constructor_throws_whenRowSplitNegative() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
SheetFreezePaneStrategy(0, -1, 0, 0));
+ }
+
+ @Test
+ void constructor_throws_whenLeftmostColumnNegative() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
SheetFreezePaneStrategy(0, 0, -1, 0));
+ }
+
+ @Test
+ void constructor_throws_whenTopRowNegative() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new
SheetFreezePaneStrategy(0, 0, 0, -1));
+ }
+
+ @Test
+ void constructor_acceptsAllZeros() {
+ Assertions.assertDoesNotThrow(() -> new SheetFreezePaneStrategy(0, 0,
0, 0));
+ Assertions.assertDoesNotThrow(() -> new SheetFreezePaneStrategy(0, 0));
+ }
+
+ @Test
+ void constructor_acceptsPositiveValues() {
+ Assertions.assertDoesNotThrow(() -> new SheetFreezePaneStrategy(2, 3,
4, 5));
+ }
+
+ @Test
+ void constructor_fromProperty_shouldNotThrowException() {
+ SheetFreezePaneProperty property = new SheetFreezePaneProperty();
+ property.setColSplit(1);
+ property.setRowSplit(2);
+ property.setLeftmostColumn(3);
+ property.setTopRow(4);
+
+ Assertions.assertDoesNotThrow(() -> new
SheetFreezePaneStrategy(property));
+ }
+
+ @Test
+ void afterSheetCreate_createsFreezePaneWithCorrectParams() {
+ SheetFreezePaneStrategy strategy = new SheetFreezePaneStrategy(2, 3,
4, 5);
+
+ WriteWorkbookHolder workbookHolder =
Mockito.mock(WriteWorkbookHolder.class);
+ WriteSheetHolder sheetHolder = Mockito.mock(WriteSheetHolder.class);
+ Sheet sheet = Mockito.mock(Sheet.class);
+ Mockito.when(sheetHolder.getSheet()).thenReturn(sheet);
+
+ strategy.afterSheetCreate(workbookHolder, sheetHolder);
+
+ Mockito.verify(sheet).createFreezePane(2, 3, 4, 5);
+ }
+
+ @Test
+ void afterSheetCreate_withZeroValues() {
+ SheetFreezePaneStrategy strategy = new SheetFreezePaneStrategy(0, 0);
+
+ WriteWorkbookHolder workbookHolder =
Mockito.mock(WriteWorkbookHolder.class);
+ WriteSheetHolder sheetHolder = Mockito.mock(WriteSheetHolder.class);
+ Sheet sheet = Mockito.mock(Sheet.class);
+ Mockito.when(sheetHolder.getSheet()).thenReturn(sheet);
+
+ strategy.afterSheetCreate(workbookHolder, sheetHolder);
+
+ Mockito.verify(sheet).createFreezePane(0, 0, 0, 0);
+ }
+
+ @Test
+ void afterSheetCreate_delegatesToSheetNotWorkbook() {
+ SheetFreezePaneStrategy strategy = new SheetFreezePaneStrategy(1, 1,
1, 1);
+
+ WriteWorkbookHolder workbookHolder =
Mockito.mock(WriteWorkbookHolder.class);
+ WriteSheetHolder sheetHolder = Mockito.mock(WriteSheetHolder.class);
+ Sheet sheet = Mockito.mock(Sheet.class);
+ Mockito.when(sheetHolder.getSheet()).thenReturn(sheet);
+
+ strategy.afterSheetCreate(workbookHolder, sheetHolder);
+
+ Mockito.verify(sheet).createFreezePane(1, 1, 1, 1);
+ Mockito.verifyNoInteractions(workbookHolder);
+ }
+}
diff --git a/website/docs/sheet/help/annotation.md
b/website/docs/sheet/help/annotation.md
index 374511e0..19287de6 100644
--- a/website/docs/sheet/help/annotation.md
+++ b/website/docs/sheet/help/annotation.md
@@ -146,3 +146,14 @@ Defines a one-time absolute merge region. The parameters
are as follows:
| lastRowIndex | -1 | The index of the last row to merge.
|
| firstColumnIndex | -1 | The index of the first column to merge.
|
| lastColumnIndex | -1 | The index of the last column to merge.
|
+
+### `@FreezePane`
+
+Define a freeze pane for an Excel sheet. The parameters are as follows:
+
+| Name | Default Value | Description
|
+|----------------|---------------|--------------------------------------------------------------------------|
+| colSplit | 0 | Horizontal position of freeze pane.
|
+| rowSplit | 0 | Vertical position of freeze pane.
|
+| leftmostColumn | -1 | Left column visible in right pane. By
default, it's equal to `colSplit`. |
+| topRow | -1 | Top row visible in bottom pane. By default,
it's equal to `rowSplit`. |
diff --git
a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/help/annotation.md
b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/help/annotation.md
index 27bc59b2..bcecf7f2 100644
---
a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/help/annotation.md
+++
b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/help/annotation.md
@@ -123,3 +123,14 @@ title: '注解'
| lastRowIndex | -1 | 合并区域的最后一行索引 |
| firstColumnIndex | -1 | 合并区域的第一列索引 |
| lastColumnIndex | -1 | 合并区域的最后一列索引 |
+
+### `@FreezePane`
+
+为 Excel 工作表定义冻结窗格。具体参数如下:
+
+| 名称 | 默认值 | 描述 |
+|----------------|-----|------------------------------------|
+| colSplit | 0 | 冻结窗格的水平位置(即需要冻结的列数) |
+| rowSplit | 0 | 冻结窗格的垂直位置(即需要冻结的行数) |
+| leftmostColumn | -1 | 右侧窗格中可见的最左侧列。默认情况下,该值等于 `colSplit` |
+| topRow | -1 | 底部窗格中可见的最顶部行。默认情况下,该值等于 `rowSplit` |
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]