You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ce...@apache.org on 2026/10/27 15:35:15 UTC

(poi) 02/03: Avoid more NPEs and ClassCastExceptions in CellFormat

This is an automated email from the ASF dual-hosted git repository.

centic9 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git

commit d51c386d706c93f2038cb4cae83be6900cb5d069
Author: Dominik Stadler <do...@gmx.at>
AuthorDate: Tue Oct 27 10:57:11 2026 +0100

    Avoid more NPEs and ClassCastExceptions in CellFormat
---
 .../apache/poi/ss/format/CellDateFormatter.java    |  2 +-
 .../apache/poi/ss/format/CellElapsedFormatter.java |  4 +++
 .../java/org/apache/poi/ss/format/CellFormat.java  | 34 +++++++++++++++-------
 .../org/apache/poi/ss/format/TestCellFormat.java   | 30 +++++++++++++++++--
 4 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java b/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java
index 7775aa2784..f7309b0be9 100644
--- a/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java
+++ b/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java
@@ -220,7 +220,7 @@ public class CellDateFormatter extends CellFormatter {
                         if (msecs < 0) {
                             msecs += 1000;
                         }
-                        formatter.format(locale, sFmt, msecs / 1000.0);
+                        formatter.format(locale, sFmt == null ? "%f" : sFmt, msecs / 1000.0);
                     }
                     toAppendTo.delete(pos, pos + 2);
                     doneMillis = true;
diff --git a/poi/src/main/java/org/apache/poi/ss/format/CellElapsedFormatter.java b/poi/src/main/java/org/apache/poi/ss/format/CellElapsedFormatter.java
index a350231fc1..d7cdd7e207 100644
--- a/poi/src/main/java/org/apache/poi/ss/format/CellElapsedFormatter.java
+++ b/poi/src/main/java/org/apache/poi/ss/format/CellElapsedFormatter.java
@@ -177,6 +177,10 @@ public class CellElapsedFormatter extends CellFormatter {
 
     @Override
     public void formatValue(StringBuffer toAppendTo, Object value) {
+        if (!(value instanceof Number)) {
+            throw new IllegalArgumentException("Expected object of type number for format " + format + ", but had: " + value);
+        }
+
         double elapsed = ((Number) value).doubleValue();
 
         if (elapsed < 0) {
diff --git a/poi/src/main/java/org/apache/poi/ss/format/CellFormat.java b/poi/src/main/java/org/apache/poi/ss/format/CellFormat.java
index 93becf26ee..77c9f257b1 100644
--- a/poi/src/main/java/org/apache/poi/ss/format/CellFormat.java
+++ b/poi/src/main/java/org/apache/poi/ss/format/CellFormat.java
@@ -468,23 +468,37 @@ public class CellFormat {
                 if ((!posNumFmt.hasCondition() && val >= 0)
                         || (posNumFmt.hasCondition() && posNumFmt.applies(val))) {
                     return posNumFmt;
-                } else if (!negNumFmt.hasCondition()
-                        || (negNumFmt.hasCondition() && negNumFmt.applies(val))) {
-                    return negNumFmt;
                 } else {
-                    // Return ###...### (255 #s) to match Excel 2007 behaviour
-                    return new CellFormatPart(QUOTE + INVALID_VALUE_FOR_FORMAT + QUOTE);
+                    if (negNumFmt == null) {
+                        throw new IllegalStateException(
+                                "Cannot apply cell format, check for warning logs during creation of format: " + format);
+                    }
+
+                    if (!negNumFmt.hasCondition()
+                            || (negNumFmt.hasCondition() && negNumFmt.applies(val))) {
+                        return negNumFmt;
+                    } else {
+                        // Return ###...### (255 #s) to match Excel 2007 behaviour
+                        return new CellFormatPart(QUOTE + INVALID_VALUE_FOR_FORMAT + QUOTE);
+                    }
                 }
             } else {
                 if ((!posNumFmt.hasCondition() && val > 0)
                         || (posNumFmt.hasCondition() && posNumFmt.applies(val))) {
                     return posNumFmt;
-                } else if ((!negNumFmt.hasCondition() && val < 0)
-                        || (negNumFmt.hasCondition() && negNumFmt.applies(val))) {
-                    return negNumFmt;
-                // Only the first two format parts can have conditions
                 } else {
-                    return zeroNumFmt;
+                    if (negNumFmt == null) {
+                        throw new IllegalStateException(
+                                "Cannot apply cell format, check for warning logs during creation of format: " + format);
+                    }
+
+                    if ((!negNumFmt.hasCondition() && val < 0)
+                            || (negNumFmt.hasCondition() && negNumFmt.applies(val))) {
+                        return negNumFmt;
+                        // Only the first two format parts can have conditions
+                    } else {
+                        return zeroNumFmt;
+                    }
                 }
             }
         } else {
diff --git a/poi/src/test/java/org/apache/poi/ss/format/TestCellFormat.java b/poi/src/test/java/org/apache/poi/ss/format/TestCellFormat.java
index e1ebd0ca3c..d7ab91dfad 100644
--- a/poi/src/test/java/org/apache/poi/ss/format/TestCellFormat.java
+++ b/poi/src/test/java/org/apache/poi/ss/format/TestCellFormat.java
@@ -30,6 +30,7 @@ import java.nio.file.Files;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.IllegalFormatConversionException;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.stream.Stream;
@@ -59,14 +60,14 @@ class TestCellFormat {
     private static TimeZone userTimeZone;
 
     @BeforeAll
-    public static void setTimeZone() {
+    static void setTimeZone() {
         userTimeZone = LocaleUtil.getUserTimeZone();
         LocaleUtil.setUserTimeZone(TimeZone.getTimeZone("CET"));
         LocaleUtil.setUserLocale(Locale.US);
     }
 
     @AfterAll
-    public static void resetTimeZone() {
+    static void resetTimeZone() {
         LocaleUtil.setUserTimeZone(userTimeZone);
         LocaleUtil.setUserLocale(Locale.ROOT);
     }
@@ -1336,4 +1337,29 @@ class TestCellFormat {
         assertThrows(IllegalArgumentException.class,
                 () -> cellFormat.apply(Double.parseDouble("-2.0579980172405396E-35")));
     }
+
+    @Test
+    void testApplyNPE3() {
+        CellFormat cellFormat = CellFormat.getInstance(Locale.ROOT, ";@\u007F??.???@;['@|';??");
+
+        assertThrows(IllegalStateException.class,
+                () -> cellFormat.apply(0.0d));
+    }
+
+    @Test
+    void testApplyNPE4() {
+        CellFormat cellFormat = CellFormat.getInstance(Locale.ROOT, "y*S_#1\u0000y*\u0000\by*\u0000\n\u0000");
+
+        CellFormatResult result = cellFormat.apply(-0.0d);
+        // exact outcome may depend on current year, do not depend on it here
+        assertNotNull(result);
+    }
+
+    @Test
+    void testApplyClassCaseException() {
+        CellFormat cellFormat = CellFormat.getInstance(Locale.ENGLISH, "|;x;\b̼;\bS\n1\\E]\\E]S[S]S[$-S]");
+
+        assertThrows(IllegalArgumentException.class,
+                () -> cellFormat.apply(""));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org