diff --git a/fesod/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java b/fesod/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java index 3b8a144d0..76c66fe0d 100644 --- a/fesod/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java +++ b/fesod/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java @@ -19,6 +19,7 @@ package org.apache.fesod.sheet.converters; +import java.util.Collections; import java.util.Map; import org.apache.fesod.sheet.converters.ConverterKeyBuild.ConverterKey; import org.apache.fesod.sheet.converters.bigdecimal.BigDecimalBooleanConverter; @@ -175,10 +176,10 @@ private static void initDefaultWriteConverter() { /** * Load default write converter * - * @return + * @return Unmodifiable map of default write converters */ public static Map> loadDefaultWriteConverter() { - return defaultWriteConverter; + return Collections.unmodifiableMap(defaultWriteConverter); } private static void putWriteConverter(Converter converter) { @@ -193,7 +194,7 @@ private static void putWriteStringConverter(Converter converter) { /** * Load default read converter * - * @return + * @return Unmodifiable map of default read converters */ public static Map> loadDefaultReadConverter() { return loadAllConverter(); @@ -202,10 +203,10 @@ public static Map> loadDefaultReadConverter() { /** * Load all converter * - * @return + * @return Unmodifiable map of all converters */ public static Map> loadAllConverter() { - return allConverter; + return Collections.unmodifiableMap(allConverter); } private static void putAllConverter(Converter converter) { diff --git a/fesod/src/main/java/org/apache/fesod/sheet/read/metadata/holder/AbstractReadHolder.java b/fesod/src/main/java/org/apache/fesod/sheet/read/metadata/holder/AbstractReadHolder.java index 779ec0c04..0e451b311 100644 --- a/fesod/src/main/java/org/apache/fesod/sheet/read/metadata/holder/AbstractReadHolder.java +++ b/fesod/src/main/java/org/apache/fesod/sheet/read/metadata/holder/AbstractReadHolder.java @@ -114,7 +114,7 @@ public AbstractReadHolder(ReadBasicParameter readBasicParameter, AbstractReadHol } if (parentAbstractReadHolder == null) { - setConverterMap(DefaultConverterLoader.loadDefaultReadConverter()); + setConverterMap(new HashMap<>(DefaultConverterLoader.loadDefaultReadConverter())); } else { setConverterMap(new HashMap<>(parentAbstractReadHolder.getConverterMap())); } diff --git a/fesod/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java b/fesod/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java index 80122ba2f..88b2ce70d 100644 --- a/fesod/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java +++ b/fesod/src/main/java/org/apache/fesod/sheet/write/metadata/holder/AbstractWriteHolder.java @@ -255,7 +255,7 @@ public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWrit // Set converterMap if (parentAbstractWriteHolder == null) { - setConverterMap(DefaultConverterLoader.loadDefaultWriteConverter()); + setConverterMap(new HashMap<>(DefaultConverterLoader.loadDefaultWriteConverter())); } else { setConverterMap(new HashMap<>(parentAbstractWriteHolder.getConverterMap())); } diff --git a/fesod/src/test/java/org/apache/fesod/sheet/converter/ConverterIsolationTest.java b/fesod/src/test/java/org/apache/fesod/sheet/converter/ConverterIsolationTest.java new file mode 100644 index 000000000..7473ecd98 --- /dev/null +++ b/fesod/src/test/java/org/apache/fesod/sheet/converter/ConverterIsolationTest.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.converter; + +import static org.junit.jupiter.api.Assertions.*; +import java.io.File; +import java.util.ArrayList; +import org.apache.fesod.sheet.ExcelReader; +import org.apache.fesod.sheet.ExcelWriter; +import org.apache.fesod.sheet.FesodSheet; +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; +import org.apache.fesod.sheet.util.TestFileUtil; +import org.junit.jupiter.api.Test; + +public class ConverterIsolationTest { + + public static class TestData {} + + public static class WriteConverterA implements Converter { + + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty p, GlobalConfiguration g) { + return new WriteCellData<>("A-" + value); + } + } + + public static class ReadConverterA implements Converter { + + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty p, GlobalConfiguration g) { + return "A-" + cellData.getStringValue(); + } + } + + @Test + public void testWriterConverterIsolation() { + ExcelWriter writer1 = FesodSheet.write(new File(TestFileUtil.getPath() + "writer1.xlsx"), TestData.class) + .registerConverter(new WriteConverterA()) + .build(); + + ExcelWriter writer2 = FesodSheet.write(new File(TestFileUtil.getPath() + "writer2.xlsx"), TestData.class) + .build(); + + boolean writer2HasConverterA = writer2.writeContext().currentWriteHolder().converterMap().values().stream() + .anyMatch(c -> c instanceof WriteConverterA); + + writer1.finish(); + writer2.finish(); + + assertFalse(writer2HasConverterA, "Custom converter should not leak between ExcelWriter instances"); + } + + @Test + public void testReaderConverterIsolation() { + File testFile = TestFileUtil.createNewFile("converter_isolation_test.xlsx"); + + FesodSheet.write(testFile, TestData.class).sheet().doWrite(new ArrayList<>()); + + ExcelReader reader1 = FesodSheet.read(testFile, TestData.class, null) + .registerConverter(new ReadConverterA()) + .build(); + + ExcelReader reader2 = FesodSheet.read(testFile, TestData.class, null).build(); + + boolean leaked = reader2.analysisContext().currentReadHolder().converterMap().values().stream() + .anyMatch(c -> c instanceof ReadConverterA); + + reader1.finish(); + reader2.finish(); + + assertFalse(leaked); + } +}