Skip to content

Commit 9db0553

Browse files
bengbengbalabalabengalaahongpsxjoydelei
authored
fix: support immutable lists in dynamic header (#668)
* fix: support immutable lists in dynamic header This prevents UnsupportedOperationException when using immutable lists such as Arrays.asList、List.of or Collections.unmodifiableList ... * test: update test based on review feedback. - Use English to describe - Avoid use imports * * refactor: replace stream implementation with explicit loop, and update JavaDoc. * chore: remove unnecessary imports. --------- Co-authored-by: ian zhang <ian.zhangzhe@gmail.com> Co-authored-by: Shuxin Pan <psxjoy@apache.org> Co-authored-by: DeleiGuo <delei@apache.org>
1 parent 136048a commit 9db0553

4 files changed

Lines changed: 224 additions & 1 deletion

File tree

fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/AbstractParameterBuilder.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.apache.fesod.sheet.metadata;
2121

22+
import java.util.ArrayList;
2223
import java.util.List;
2324
import java.util.Locale;
2425
import java.util.Objects;
@@ -39,10 +40,27 @@ public abstract class AbstractParameterBuilder<T extends AbstractParameterBuilde
3940
* @return
4041
*/
4142
public T head(List<List<String>> head) {
42-
parameter().setHead(head);
43+
parameter().setHead(toMutableListIfNecessary(head));
4344
return self();
4445
}
4546

47+
/**
48+
* Ensures and returns a mutable head list.
49+
*
50+
* @param head The source list to create a mutable from.
51+
* @return A new mutable list, or the original list if the input is null or empty.
52+
*/
53+
private List<List<String>> toMutableListIfNecessary(List<List<String>> head) {
54+
if (null == head || head.isEmpty()) {
55+
return head;
56+
}
57+
List<List<String>> result = new ArrayList<>();
58+
for (List<String> headColumn : head) {
59+
result.add(new ArrayList<>(headColumn));
60+
}
61+
return result;
62+
}
63+
4664
/**
4765
* You can only choose one of the {@link #head(List)} and {@link #head(Class)}
4866
*
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.fesod.sheet.head;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.Map;
25+
import org.apache.fesod.sheet.context.AnalysisContext;
26+
import org.apache.fesod.sheet.metadata.data.ReadCellData;
27+
import org.apache.fesod.sheet.read.listener.ReadListener;
28+
import org.junit.jupiter.api.Assertions;
29+
30+
public class ImmutableListHeadDataListener implements ReadListener<Map<Integer, String>> {
31+
32+
private List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>();
33+
34+
@Override
35+
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
36+
Assertions.assertNotNull(context.readRowHolder().getRowIndex());
37+
headMap.forEach((key, value) -> {
38+
Assertions.assertEquals(value.getRowIndex(), context.readRowHolder().getRowIndex());
39+
Assertions.assertEquals(value.getColumnIndex(), key);
40+
});
41+
}
42+
43+
@Override
44+
public void invoke(Map<Integer, String> data, AnalysisContext context) {
45+
list.add(data);
46+
}
47+
48+
@Override
49+
public void doAfterAllAnalysed(AnalysisContext context) {
50+
List<List<String>> head = context.readSheetHolder().getHead();
51+
52+
Assertions.assertInstanceOf(ArrayList.class, head);
53+
for (List<String> item : head) {
54+
Assertions.assertInstanceOf(ArrayList.class, item);
55+
}
56+
57+
Assertions.assertEquals(1, list.size());
58+
Map<Integer, String> data = list.get(0);
59+
Assertions.assertEquals("stringData", data.get(0));
60+
Assertions.assertEquals("1", data.get(1));
61+
Assertions.assertEquals("2025-10-31 01:01:01", data.get(2));
62+
Assertions.assertEquals("extraData", data.get(3));
63+
}
64+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.fesod.sheet.head;
21+
22+
import java.io.File;
23+
import java.text.ParseException;
24+
import java.util.ArrayList;
25+
import java.util.Arrays;
26+
import java.util.Collections;
27+
import java.util.List;
28+
import org.apache.fesod.sheet.FastExcel;
29+
import org.apache.fesod.sheet.util.DateUtils;
30+
import org.apache.fesod.sheet.util.TestFileUtil;
31+
import org.junit.jupiter.api.BeforeAll;
32+
import org.junit.jupiter.api.MethodOrderer;
33+
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.api.TestMethodOrder;
35+
36+
@TestMethodOrder(MethodOrderer.MethodName.class)
37+
public class ImmutableListHeadDataTest {
38+
39+
private static File file07;
40+
private static File file03;
41+
private static File fileCsv;
42+
43+
@BeforeAll
44+
public static void init() {
45+
file07 = TestFileUtil.createNewFile("listHead07.xlsx");
46+
file03 = TestFileUtil.createNewFile("listHead03.xls");
47+
fileCsv = TestFileUtil.createNewFile("listHeadCsv.csv");
48+
}
49+
50+
@Test
51+
public void t01ReadAndWrite07() throws Exception {
52+
readAndWrite(file07);
53+
}
54+
55+
@Test
56+
public void t02ReadAndWrite03() throws Exception {
57+
readAndWrite(file03);
58+
}
59+
60+
@Test
61+
public void t03ReadAndWriteCsv() throws Exception {
62+
readAndWrite(fileCsv);
63+
}
64+
65+
private void readAndWrite(File file) throws Exception {
66+
FastExcel.write(file)
67+
.head(head())
68+
.registerWriteHandler(new ImmutableListHeadDataWriteHandler())
69+
.sheet()
70+
.doWrite(data());
71+
72+
FastExcel.read(file)
73+
.head(head())
74+
.registerReadListener(new ImmutableListHeadDataListener())
75+
.sheet()
76+
.doRead();
77+
}
78+
79+
private List<List<String>> head() {
80+
List<List<String>> list = new ArrayList<List<String>>();
81+
List<String> head0 = Arrays.asList("stringTitle");
82+
List<String> head1 = new ArrayList<String>();
83+
head1.add("numberTitle1");
84+
head1.add("numberTitle2");
85+
86+
list.add(head0);
87+
list.add(Collections.unmodifiableList(head1));
88+
list.add(Collections.singletonList("datetimeTitle"));
89+
return list;
90+
}
91+
92+
private List<List<Object>> data() throws ParseException {
93+
List<List<Object>> list = new ArrayList<List<Object>>();
94+
List<Object> data0 = new ArrayList<Object>();
95+
data0.add("stringData");
96+
data0.add(1);
97+
data0.add(DateUtils.parseDate("2025-10-31 01:01:01"));
98+
data0.add("extraData");
99+
list.add(data0);
100+
return list;
101+
}
102+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.fesod.sheet.head;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import org.apache.fesod.sheet.write.handler.SheetWriteHandler;
25+
import org.apache.fesod.sheet.write.handler.context.SheetWriteHandlerContext;
26+
import org.junit.jupiter.api.Assertions;
27+
28+
public class ImmutableListHeadDataWriteHandler implements SheetWriteHandler {
29+
30+
@Override
31+
public void afterSheetDispose(SheetWriteHandlerContext context) {
32+
List<List<String>> head = context.getWriteContext().writeSheetHolder().getHead();
33+
34+
Assertions.assertInstanceOf(ArrayList.class, head);
35+
for (List<String> item : head) {
36+
Assertions.assertInstanceOf(ArrayList.class, item);
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)