Skip to content

Commit b003432

Browse files
committed
feat: introduce HeaderMergeStrategy for flexible header merging and fix issue #666
1 parent b8035d2 commit b003432

File tree

11 files changed

+694
-88
lines changed

11 files changed

+694
-88
lines changed

ISSUE_666_ANALYSIS.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# Issue #666 问题分析与解决方案
2+
3+
## 问题描述
4+
5+
在使用动态表头写入时,C2 和 D2 单元格被错误地自动合并。根据 issue #666 的描述:
6+
7+
### 测试用例
8+
```java
9+
List<List<String>> multiHeader = new ArrayList<>();
10+
multiHeader.add(new ArrayList<>(Arrays.asList("head10")));
11+
multiHeader.add(new ArrayList<>(Arrays.asList("head20", "head21")));
12+
multiHeader.add(new ArrayList<>(Arrays.asList("head30", "head31")));
13+
multiHeader.add(new ArrayList<>(Arrays.asList("head40", "head31")));
14+
multiHeader.add(new ArrayList<>(Arrays.asList("head40", "head41")));
15+
```
16+
17+
### 表头结构(按列视角)
18+
- **列0**: `["head10", "head20", "head30", "head40", "head40"]`
19+
- **列1**: `[null, "head21", "head31", "head31", "head41"]`
20+
21+
### 问题现象
22+
- **当前行为**: C2 和 D2 被自动合并
23+
- **预期行为**: C2 和 D2 不应该合并
24+
25+
## 根本原因分析
26+
27+
### 当前合并算法的问题
28+
29+
`ExcelWriteHeadProperty.headCellRangeList()` 方法中(第113-160行),合并算法存在以下问题:
30+
31+
1. **缺乏矩形区域验证**
32+
- 算法会先水平查找(向右扩展),然后垂直查找(向下扩展)
33+
- 但没有验证整个矩形区域内的所有单元格是否都具有相同的名称
34+
- 这可能导致不完整的矩形区域被错误地合并
35+
36+
2. **垂直合并逻辑缺陷**
37+
- 当处理第3行的 "head31"(列1,行2)时:
38+
- 算法先水平查找,发现没有相邻的相同名称
39+
- 然后垂直向下查找,发现第4行(列1,行3)也有 "head31"
40+
- 算法会创建一个合并区域:行2-3,列1-1
41+
- **问题**:虽然两行的 "head31" 名称相同,但它们上方单元格的上下文不同(第3行上方是 "head30",第4行上方是 "head40"),不应该合并
42+
43+
3. **缺乏合并策略控制**
44+
- 当前只有 `automaticMergeHead` 布尔参数,只能开启/关闭自动合并
45+
- 无法精细控制合并行为(例如:只允许水平合并、只允许垂直合并、只允许完整矩形合并等)
46+
47+
## 解决方案设计
48+
49+
### 1. 引入 HeaderMergeStrategy 枚举
50+
51+
创建一个枚举类型来控制合并策略:
52+
53+
```java
54+
public enum HeaderMergeStrategy {
55+
/**
56+
* 不进行任何自动合并
57+
*/
58+
NONE,
59+
60+
/**
61+
* 仅水平合并(同一行内的相同单元格)
62+
*/
63+
HORIZONTAL_ONLY,
64+
65+
/**
66+
* 仅垂直合并(同一列内的相同单元格)
67+
*/
68+
VERTICAL_ONLY,
69+
70+
/**
71+
* 仅完整的矩形区域合并(所有单元格必须形成完整的矩形且名称相同)
72+
*/
73+
FULL_RECTANGLE,
74+
75+
/**
76+
* 自动合并(当前默认行为,向后兼容)
77+
*/
78+
AUTO
79+
}
80+
```
81+
82+
### 2. 增强 API 以支持合并策略配置
83+
84+
#### 2.1 在 WriteBasicParameter 中添加新字段
85+
86+
```java
87+
/**
88+
* 表头合并策略
89+
*/
90+
private HeaderMergeStrategy headerMergeStrategy;
91+
```
92+
93+
#### 2.2 在 Builder 中添加配置方法
94+
95+
```java
96+
/**
97+
* 设置表头合并策略
98+
*
99+
* @param strategy 合并策略
100+
* @return this
101+
*/
102+
public T headerMergeStrategy(HeaderMergeStrategy strategy) {
103+
parameter().setHeaderMergeStrategy(strategy);
104+
return self();
105+
}
106+
```
107+
108+
#### 2.3 保持向后兼容性
109+
110+
- 如果 `headerMergeStrategy``null`,则根据 `automaticMergeHead` 决定:
111+
- `automaticMergeHead == true``HeaderMergeStrategy.AUTO`
112+
- `automaticMergeHead == false``HeaderMergeStrategy.NONE`
113+
- 如果 `headerMergeStrategy` 不为 `null`,则使用新策略,忽略 `automaticMergeHead`
114+
115+
### 3. 改进合并算法
116+
117+
#### 3.1 矩形区域有效性验证
118+
119+
在合并之前,需要验证:
120+
1. **矩形完整性**:确保矩形区域内的所有单元格都存在且名称相同
121+
2. **上下文一致性**:对于垂直合并,需要验证上方单元格的上下文是否一致
122+
3. **边界检查**:确保合并区域不超出表头边界
123+
124+
#### 3.2 不同策略的实现
125+
126+
- **NONE**: 不执行任何合并
127+
- **HORIZONTAL_ONLY**: 只合并同一行内的相邻相同单元格
128+
- **VERTICAL_ONLY**: 只合并同一列内的相邻相同单元格(需验证上下文)
129+
- **FULL_RECTANGLE**: 只合并完整的矩形区域(所有单元格名称相同)
130+
- **AUTO**: 保持当前行为(向后兼容),但添加矩形验证
131+
132+
### 4. 算法改进示例
133+
134+
#### 改进后的合并逻辑(FULL_RECTANGLE 策略)
135+
136+
```java
137+
private boolean isValidRectangleRegion(
138+
List<Head> headList,
139+
int startRow, int endRow,
140+
int startCol, int endCol,
141+
String expectedName) {
142+
// 验证矩形区域内的所有单元格
143+
for (int row = startRow; row <= endRow; row++) {
144+
for (int col = startCol; col <= endCol; col++) {
145+
if (row >= headList.get(col).getHeadNameList().size()) {
146+
return false; // 单元格不存在
147+
}
148+
String cellName = headList.get(col).getHeadNameList().get(row);
149+
if (!expectedName.equals(cellName)) {
150+
return false; // 单元格名称不匹配
151+
}
152+
}
153+
}
154+
return true;
155+
}
156+
```
157+
158+
#### 垂直合并的上下文验证
159+
160+
```java
161+
private boolean canMergeVertically(
162+
List<Head> headList,
163+
int row1, int row2,
164+
int col,
165+
String cellName) {
166+
// 检查上方单元格上下文是否一致
167+
if (row1 > 0 && row2 > 0) {
168+
String upper1 = headList.get(col).getHeadNameList().get(row1 - 1);
169+
String upper2 = headList.get(col).getHeadNameList().get(row2 - 1);
170+
if (!Objects.equals(upper1, upper2)) {
171+
return false; // 上下文不一致,不能合并
172+
}
173+
}
174+
return true;
175+
}
176+
```
177+
178+
## 实施步骤
179+
180+
### 阶段1:创建枚举和基本结构
181+
1. 创建 `HeaderMergeStrategy` 枚举类
182+
2.`WriteBasicParameter` 中添加 `headerMergeStrategy` 字段
183+
3.`AbstractExcelWriterParameterBuilder` 中添加配置方法
184+
185+
### 阶段2:实现合并策略逻辑
186+
1. 修改 `ExcelWriteHeadProperty.headCellRangeList()` 方法,支持不同策略
187+
2. 实现矩形区域验证方法
188+
3. 实现上下文验证方法
189+
190+
### 阶段3:向后兼容处理
191+
1.`AbstractWriteHolder` 中处理策略的默认值
192+
2. 确保 `automaticMergeHead` 参数仍然有效
193+
194+
### 阶段4:测试和文档
195+
1. 为 issue #666 创建测试用例
196+
2. 添加其他策略的测试用例
197+
3. 更新文档
198+
199+
## 代码修改点
200+
201+
### 需要修改的文件
202+
203+
1. **新建文件**
204+
- `fesod/src/main/java/org/apache/fesod/excel/enums/HeaderMergeStrategy.java`
205+
206+
2. **修改文件**
207+
- `fesod/src/main/java/org/apache/fesod/excel/write/metadata/WriteBasicParameter.java`
208+
- `fesod/src/main/java/org/apache/fesod/excel/write/builder/AbstractExcelWriterParameterBuilder.java`
209+
- `fesod/src/main/java/org/apache/fesod/excel/write/property/ExcelWriteHeadProperty.java`
210+
- `fesod/src/main/java/org/apache/fesod/excel/write/metadata/holder/AbstractWriteHolder.java`
211+
- `fesod/src/main/java/org/apache/fesod/excel/write/metadata/holder/WriteHolder.java`
212+
- `fesod/src/main/java/org/apache/fesod/excel/context/WriteContextImpl.java`
213+
214+
3. **测试文件**
215+
- 新建测试用例验证 issue #666 的场景
216+
- 添加各种策略的测试用例
217+
218+
## 预期效果
219+
220+
1. **修复 issue #666**:C2 和 D2 不再被错误合并
221+
2. **增强灵活性**:用户可以根据需要选择不同的合并策略
222+
3. **向后兼容**:现有的 `automaticMergeHead` 参数仍然有效
223+
4. **提高准确性**:通过矩形验证,避免错误的合并
224+
225+
## 注意事项
226+
227+
1. **性能考虑**:矩形验证会增加一些计算开销,但影响应该很小
228+
2. **边界情况**:需要处理空表头、单行表头、单列表头等特殊情况
229+
3. **文档更新**:需要在用户文档中说明新的合并策略选项

0 commit comments

Comments
 (0)