diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverter.java index 50a27cf4e..3e40ffb82 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverter.java @@ -53,7 +53,8 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Byte convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getNumberValue().byteValue(); + return NumberUtils.narrowInRange(cellData.getNumberValue(), Byte.MIN_VALUE, Byte.MAX_VALUE) + .byteValueExact(); } @Override diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverter.java index 628b4a1b5..d602821d1 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverter.java @@ -54,7 +54,8 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Integer convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getNumberValue().intValue(); + return NumberUtils.narrowInRange(cellData.getNumberValue(), Integer.MIN_VALUE, Integer.MAX_VALUE) + .intValueExact(); } @Override diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverter.java index 83e2b1350..e83ba1099 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverter.java @@ -54,7 +54,8 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Long convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getNumberValue().longValue(); + return NumberUtils.narrowInRange(cellData.getNumberValue(), Long.MIN_VALUE, Long.MAX_VALUE) + .longValueExact(); } @Override diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverter.java index be290348e..8f699cc18 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverter.java @@ -54,7 +54,8 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Short convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getNumberValue().shortValue(); + return NumberUtils.narrowInRange(cellData.getNumberValue(), Short.MIN_VALUE, Short.MAX_VALUE) + .shortValueExact(); } @Override diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/NumberUtils.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/NumberUtils.java index 4d5163700..9e59b2ecb 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/NumberUtils.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/NumberUtils.java @@ -109,6 +109,27 @@ public static WriteCellData formatToCellData(Number num, ExcelContentProperty return cellData; } + /** + * Narrow a cell number to an integral value in the closed range [{@code minValue}, {@code maxValue}]. The + * fractional part is truncated toward zero, matching the previous narrowing behavior; a value whose + * truncated integer part falls outside the range throws {@link ArithmeticException} instead of silently + * wrapping around. + * + * @param value the cell number + * @param minValue the inclusive lower bound of the target type + * @param maxValue the inclusive upper bound of the target type + * @return the truncated value, guaranteed to be integral and within the range + */ + public static BigDecimal narrowInRange(BigDecimal value, long minValue, long maxValue) { + BigDecimal truncated = value.setScale(0, RoundingMode.DOWN); + if (truncated.compareTo(BigDecimal.valueOf(minValue)) < 0 + || truncated.compareTo(BigDecimal.valueOf(maxValue)) > 0) { + throw new ArithmeticException( + truncated + " is out of the required range [" + minValue + ", " + maxValue + "]"); + } + return truncated; + } + /** * parse * @@ -118,9 +139,12 @@ public static WriteCellData formatToCellData(Number num, ExcelContentProperty */ public static Short parseShort(String string, ExcelContentProperty contentProperty) throws ParseException { if (!hasFormat(contentProperty)) { - return new BigDecimal(string).shortValue(); + return narrowInRange(new BigDecimal(string), Short.MIN_VALUE, Short.MAX_VALUE) + .shortValueExact(); } - return parse(string, contentProperty).shortValue(); + return narrowInRange( + new BigDecimal(parse(string, contentProperty).toString()), Short.MIN_VALUE, Short.MAX_VALUE) + .shortValueExact(); } /** @@ -132,9 +156,11 @@ public static Short parseShort(String string, ExcelContentProperty contentProper */ public static Long parseLong(String string, ExcelContentProperty contentProperty) throws ParseException { if (!hasFormat(contentProperty)) { - return new BigDecimal(string).longValue(); + return narrowInRange(new BigDecimal(string), Long.MIN_VALUE, Long.MAX_VALUE) + .longValueExact(); } - return parse(string, contentProperty).longValue(); + return narrowInRange(new BigDecimal(parse(string, contentProperty).toString()), Long.MIN_VALUE, Long.MAX_VALUE) + .longValueExact(); } /** @@ -146,9 +172,12 @@ public static Long parseLong(String string, ExcelContentProperty contentProperty */ public static Integer parseInteger(String string, ExcelContentProperty contentProperty) throws ParseException { if (!hasFormat(contentProperty)) { - return new BigDecimal(string).intValue(); + return narrowInRange(new BigDecimal(string), Integer.MIN_VALUE, Integer.MAX_VALUE) + .intValueExact(); } - return parse(string, contentProperty).intValue(); + return narrowInRange( + new BigDecimal(parse(string, contentProperty).toString()), Integer.MIN_VALUE, Integer.MAX_VALUE) + .intValueExact(); } /** @@ -189,9 +218,11 @@ public static BigDecimal parseBigDecimal(String string, ExcelContentProperty con */ public static Byte parseByte(String string, ExcelContentProperty contentProperty) throws ParseException { if (!hasFormat(contentProperty)) { - return new BigDecimal(string).byteValue(); + return narrowInRange(new BigDecimal(string), Byte.MIN_VALUE, Byte.MAX_VALUE) + .byteValueExact(); } - return parse(string, contentProperty).byteValue(); + return narrowInRange(new BigDecimal(parse(string, contentProperty).toString()), Byte.MIN_VALUE, Byte.MAX_VALUE) + .byteValueExact(); } /** diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverterTest.java new file mode 100644 index 000000000..79c615434 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/byteconverter/ByteNumberConverterTest.java @@ -0,0 +1,63 @@ +/* + * 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.converters.byteconverter; + +import java.math.BigDecimal; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.testkit.Tags; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link ByteNumberConverter}. + */ +@Tag(Tags.UNIT) +class ByteNumberConverterTest { + + private final ByteNumberConverter converter = new ByteNumberConverter(); + + @Test + void convertToJavaDataAcceptsInRangeNumber() { + Byte actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("100")), null, null); + Assertions.assertEquals((byte) 100, actual); + } + + @Test + void convertToJavaDataTruncatesFractionLikeBefore() { + Byte actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("12.9")), null, null); + Assertions.assertEquals((byte) 12, actual); + } + + @Test + void convertToJavaDataAcceptsBoundaryValues() { + Assertions.assertEquals( + Byte.MAX_VALUE, converter.convertToJavaData(new ReadCellData<>(new BigDecimal("127")), null, null)); + Assertions.assertEquals( + Byte.MIN_VALUE, converter.convertToJavaData(new ReadCellData<>(new BigDecimal("-128")), null, null)); + } + + @Test + void convertToJavaDataRejectsOutOfRangeNumber() { + Assertions.assertThrows( + ArithmeticException.class, + () -> converter.convertToJavaData(new ReadCellData<>(new BigDecimal("300")), null, null)); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverterTest.java new file mode 100644 index 000000000..0f52551bd --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/integer/IntegerNumberConverterTest.java @@ -0,0 +1,65 @@ +/* + * 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.converters.integer; + +import java.math.BigDecimal; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.testkit.Tags; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link IntegerNumberConverter}. + */ +@Tag(Tags.UNIT) +class IntegerNumberConverterTest { + + private final IntegerNumberConverter converter = new IntegerNumberConverter(); + + @Test + void convertToJavaDataAcceptsInRangeNumber() { + Integer actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("100")), null, null); + Assertions.assertEquals(100, actual); + } + + @Test + void convertToJavaDataTruncatesFractionLikeBefore() { + Integer actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("123.9")), null, null); + Assertions.assertEquals(123, actual); + } + + @Test + void convertToJavaDataAcceptsBoundaryValues() { + Assertions.assertEquals( + Integer.MAX_VALUE, + converter.convertToJavaData(new ReadCellData<>(new BigDecimal("2147483647")), null, null)); + Assertions.assertEquals( + Integer.MIN_VALUE, + converter.convertToJavaData(new ReadCellData<>(new BigDecimal("-2147483648")), null, null)); + } + + @Test + void convertToJavaDataRejectsOutOfRangeNumber() { + Assertions.assertThrows( + ArithmeticException.class, + () -> converter.convertToJavaData(new ReadCellData<>(new BigDecimal("13800138000")), null, null)); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverterTest.java new file mode 100644 index 000000000..981a0c7cc --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/longconverter/LongNumberConverterTest.java @@ -0,0 +1,66 @@ +/* + * 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.converters.longconverter; + +import java.math.BigDecimal; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.testkit.Tags; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link LongNumberConverter}. + */ +@Tag(Tags.UNIT) +class LongNumberConverterTest { + + private final LongNumberConverter converter = new LongNumberConverter(); + + @Test + void convertToJavaDataAcceptsInRangeNumber() { + Long actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("13800138000")), null, null); + Assertions.assertEquals(13800138000L, actual); + } + + @Test + void convertToJavaDataTruncatesFractionLikeBefore() { + Long actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("123.9")), null, null); + Assertions.assertEquals(123L, actual); + } + + @Test + void convertToJavaDataAcceptsBoundaryValues() { + Assertions.assertEquals( + Long.MAX_VALUE, + converter.convertToJavaData(new ReadCellData<>(new BigDecimal("9223372036854775807")), null, null)); + Assertions.assertEquals( + Long.MIN_VALUE, + converter.convertToJavaData(new ReadCellData<>(new BigDecimal("-9223372036854775808")), null, null)); + } + + @Test + void convertToJavaDataRejectsOutOfRangeNumber() { + Assertions.assertThrows( + ArithmeticException.class, + () -> converter.convertToJavaData( + new ReadCellData<>(new BigDecimal("9223372036854775808")), null, null)); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverterTest.java new file mode 100644 index 000000000..9bbce6c4f --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/shortconverter/ShortNumberConverterTest.java @@ -0,0 +1,63 @@ +/* + * 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.converters.shortconverter; + +import java.math.BigDecimal; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.testkit.Tags; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link ShortNumberConverter}. + */ +@Tag(Tags.UNIT) +class ShortNumberConverterTest { + + private final ShortNumberConverter converter = new ShortNumberConverter(); + + @Test + void convertToJavaDataAcceptsInRangeNumber() { + Short actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("100")), null, null); + Assertions.assertEquals((short) 100, actual); + } + + @Test + void convertToJavaDataTruncatesFractionLikeBefore() { + Short actual = converter.convertToJavaData(new ReadCellData<>(new BigDecimal("123.9")), null, null); + Assertions.assertEquals((short) 123, actual); + } + + @Test + void convertToJavaDataAcceptsBoundaryValues() { + Assertions.assertEquals( + Short.MAX_VALUE, converter.convertToJavaData(new ReadCellData<>(new BigDecimal("32767")), null, null)); + Assertions.assertEquals( + Short.MIN_VALUE, converter.convertToJavaData(new ReadCellData<>(new BigDecimal("-32768")), null, null)); + } + + @Test + void convertToJavaDataRejectsOutOfRangeNumber() { + Assertions.assertThrows( + ArithmeticException.class, + () -> converter.convertToJavaData(new ReadCellData<>(new BigDecimal("40000")), null, null)); + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java index 4e16b9567..441febbd6 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java @@ -390,4 +390,35 @@ void test_readAndWriteFinishRemoveThreadLocalCache(@TempDir Path tempDir) FesodSheet.read(file).sheet().doReadSync(); Assertions.assertNull(threadLocal.get()); } + + @Test + void test_parseInteger_rejectsOutOfRangeValue() { + Assertions.assertThrows(ArithmeticException.class, () -> NumberUtils.parseInteger("13800138000", null)); + } + + @Test + void test_parseInteger_acceptsBoundaryValues() throws ParseException { + Assertions.assertEquals(Integer.MAX_VALUE, NumberUtils.parseInteger("2147483647", null)); + Assertions.assertEquals(Integer.MIN_VALUE, NumberUtils.parseInteger("-2147483648", null)); + } + + @Test + void test_parseInteger_truncatesFractionLikeBefore() throws ParseException { + Assertions.assertEquals(123, NumberUtils.parseInteger("123.9", null)); + } + + @Test + void test_parseLong_rejectsOutOfRangeValue() { + Assertions.assertThrows(ArithmeticException.class, () -> NumberUtils.parseLong("9223372036854775808", null)); + } + + @Test + void test_parseShort_rejectsOutOfRangeValue() { + Assertions.assertThrows(ArithmeticException.class, () -> NumberUtils.parseShort("40000", null)); + } + + @Test + void test_parseByte_rejectsOutOfRangeValue() { + Assertions.assertThrows(ArithmeticException.class, () -> NumberUtils.parseByte("300", null)); + } }