You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2026/06/24 15:49:14 UTC

(causeway) branch main updated: CAUSEWAY-4036: validate digit count constraints

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

andi-huber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/main by this push:
     new 5408d140ce5 CAUSEWAY-4036: validate digit count constraints
5408d140ce5 is described below

commit 5408d140ce53d640b10cee9b99d5b1001bc3664f
Author: ahuber@apache.org <ah...@192.168.0.235>
AuthorDate: Wed Jun 24 17:49:03 2026 +0200

    CAUSEWAY-4036: validate digit count constraints
---
 .../NumericValueSemanticsAbstract.java             | 61 ++++++++++++++++------
 1 file changed, 46 insertions(+), 15 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/NumericValueSemanticsAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/NumericValueSemanticsAbstract.java
index bab9b79d6db..ac9ffd97caa 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/NumericValueSemanticsAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/valuesemantics/NumericValueSemanticsAbstract.java
@@ -69,7 +69,7 @@ public String htmlPresentation(final Context context, final T value) {
     @Override
     public final String parseableTextRepresentation(final Context context, final T value) {
         return value!=null
-            ? getNumberFormat(context, FormatUsageFor.PARSING).format(value)
+            ? getNumberFormat(context, FormatUsageFor.RENDERING_AS_TEXT).format(value)
             : null;
     }
 
@@ -99,6 +99,20 @@ protected void configureDecimalFormat(
         if(feature==null)
             return;
 
+        // lookup meta-model for any facets that provide the MaximumIntegerDigits;
+        // applies only to input (we don't want to remove most significant digits when rendering)
+        // @see ValueSemantics#maxIntegerDigits
+        if(usedFor.isParsing()) {
+            Facets.maxIntegerDigits(feature)
+                .ifPresent(format::setMaximumIntegerDigits);
+        }
+
+        // lookup meta-model for any facets that provide the MinimumIntegerDigits;
+        // applies to display and input prompting alike,
+        // @see ValueSemantics#minIntegerDigits
+        Facets.minIntegerDigits(feature)
+            .ifPresent(format::setMinimumIntegerDigits);
+
         if(isFloatingPoint()) {
             // lookup meta-model for any facets that provide the MaximumFractionDigits;
             // applies to display and input alike
@@ -113,15 +127,6 @@ protected void configureDecimalFormat(
             Facets.minFractionalDigits(feature)
                 .ifPresent(format::setMinimumFractionDigits);
         }
-
-        switch (usedFor) {
-            case RENDERING_AS_TEXT, RENDERING_AS_HTML -> {
-
-            }
-            case PARSING -> {
-
-            }
-        }
     }
 
     protected Optional<BigInteger> parseInteger(
@@ -151,14 +156,26 @@ protected Optional<BigDecimal> parseDecimal(
 
         try {
             var number = formatEx.parse(input);
+            int integerDigitCount = integerDigitCount(number);
+            int fractionDigitCount = fractionDigitCount(number);
 
-            // check for maxFractionDigits if required ...
-            final int maxFractionDigits = formatEx.format().getMaximumFractionDigits();
-            if(maxFractionDigits>-1
-                    && number.scale()>formatEx.format().getMaximumFractionDigits())
+            // check for maxIntegerDigits
+            if(integerDigitCount > formatEx.maxIntegerDigits())
+                throw new TextEntryParseException(String.format(
+                    "No more than %d integer digits (digits before the decimal separator) can be entered, "
+                            + "got %d in '%s'.", formatEx.maxIntegerDigits(), integerDigitCount, input));
+
+            // check for minIntegerDigits
+            if(integerDigitCount < formatEx.minIntegerDigits())
+                throw new TextEntryParseException(String.format(
+                    "A minimum of %d integer digits (digits before the decimal separator) must be entered, "
+                            + "got %d in '%s'.", formatEx.minIntegerDigits(), integerDigitCount, input));
+
+            // check for maxFractionDigits
+            if(fractionDigitCount>formatEx.maxFractionDigits())
                 throw new TextEntryParseException(String.format(
                         "No more than %d digits can be entered after the decimal separator, "
-                                + "got %d in '%s'.", maxFractionDigits, number.scale(), input));
+                                + "got %d in '%s'.", formatEx.maxFractionDigits(), fractionDigitCount, input));
             return Optional.of(number);
         } catch (final NumberFormatException | ParseException e) {
             throw new TextEntryParseException(String.format(
@@ -167,6 +184,16 @@ protected Optional<BigDecimal> parseDecimal(
         }
     }
 
+    static int fractionDigitCount(final BigDecimal number) {
+        return number.scale()>=0
+            ? number.scale()
+            : 0;
+    }
+
+    static int integerDigitCount(final BigDecimal number) {
+        return number.toBigInteger().abs().toString().length();
+    }
+
     /**
      * Conditionally disallows the user- or system-locale specific grouping (thousands) separator
      * (being part of the input string) or at least validates, that the grouping is correctly placed.
@@ -278,6 +305,10 @@ else if (position.getIndex() < input.length())
                 throw new ParseException("input='" + rawInput + "' was not processed completely", position.getIndex());
             return decimal;
         }
+        public int maxIntegerDigits() { return format.getMaximumIntegerDigits(); }
+        public int minIntegerDigits() { return format.getMinimumIntegerDigits(); }
+        public int maxFractionDigits() { return format.getMaximumFractionDigits(); }
+        public int minFractionDigits() { return format.getMinimumFractionDigits(); }
     }
 
     /**