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/25 08:58:09 UTC

(causeway) branch main updated: CAUSEWAY-4036: honor total digits 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 531e1034905 CAUSEWAY-4036: honor total digits constraints
531e1034905 is described below

commit 531e1034905d7895174ba8a84329ad33f758e501
Author: ahuber@apache.org <ah...@10.21.43.60>
AuthorDate: Thu Jun 25 10:58:04 2026 +0200

    CAUSEWAY-4036: honor total digits constraints
---
 .../NumericValueSemanticsAbstract.java             | 54 +++++++++++++++-------
 1 file changed, 38 insertions(+), 16 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 4a387c36f49..3a6bb59e46f 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
@@ -43,6 +43,7 @@
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.io.TextUtils;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectFeature;
 import org.apache.causeway.core.metamodel.util.Facets;
 
 public abstract class NumericValueSemanticsAbstract<T>
@@ -93,8 +94,7 @@ protected void configureDecimalFormat(
             final FormatUsageFor usedFor) {
         if(context==null)
             return;
-        var feature = MetaModelContext.instanceElseFail().getSpecificationLoader()
-            .loadFeature(context.featureIdentifier())
+        var feature = feature(context)
             .orElse(null);
         if(feature==null)
             return;
@@ -129,6 +129,11 @@ protected void configureDecimalFormat(
         }
     }
 
+    private Optional<ObjectFeature> feature(final ValueSemanticsProvider.@Nullable Context context) {
+        return MetaModelContext.instanceElseFail().getSpecificationLoader()
+            .loadFeature(context.featureIdentifier());
+    }
+
     protected Optional<BigInteger> parseInteger(
             final @Nullable Context context,
             final @Nullable String text) {
@@ -176,6 +181,23 @@ protected Optional<BigDecimal> parseDecimal(
                 throw new TextEntryParseException(String.format(
                         "No more than %d digits can be entered after the decimal separator, "
                                 + "got %d in '%s'.", formatEx.maxFractionDigits(), fractionDigitCount, input));
+
+            // lookup meta-model for any facets that provide the TotalDigits
+            // @see ValueSemantics#maxTotalDigits
+            {
+                int maxTotalDigitsExplicit = feature(context)
+                    .map(Facets::maxTotalDigitsExplicit)
+                    .map(it->it.orElse(-1))
+                    .orElse(-1);
+                int totalDigitCount = integerDigitCount + fractionDigitCount;
+                if(maxTotalDigitsExplicit>0
+                    && totalDigitCount > maxTotalDigitsExplicit) {
+                        throw new TextEntryParseException(String.format(
+                            "No more than %d digits can be entered in total, "
+                                    + "got %d in '%s'.", maxTotalDigitsExplicit, totalDigitCount, input));
+                }
+            }
+
             return Optional.of(number);
         } catch (final NumberFormatException | ParseException e) {
             throw new TextEntryParseException(String.format(
@@ -252,6 +274,20 @@ protected void validateNumericalInput(
         }
     }
 
+    /**
+     * @return {@link NumberFormat} the default from given context's locale
+     *      or else system's default locale
+     */
+    protected DecimalFormatEx getNumberFormat(
+        final ValueSemanticsProvider.@Nullable Context context,
+        final @NonNull FormatUsageFor usedFor) {
+        var format = NumericValueSemantics.localeDecimalFormat(context);
+        // prime as unconstraint
+        format.setMaximumFractionDigits(340);
+        configureDecimalFormat(context, format, usedFor);
+        return DecimalFormatEx.of(format, grouping().separator(context, usedFor));
+    }
+
     // -- HELPER
 
     protected record DecimalFormatEx(
@@ -311,20 +347,6 @@ else if (position.getIndex() < input.length())
         public int minFractionDigits() { return format.getMinimumFractionDigits(); }
     }
 
-    /**
-     * @return {@link NumberFormat} the default from given context's locale
-     *      or else system's default locale
-     */
-    protected DecimalFormatEx getNumberFormat(
-            final ValueSemanticsProvider.@Nullable Context context,
-            final @NonNull FormatUsageFor usedFor) {
-        var format = NumericValueSemantics.localeDecimalFormat(context);
-        // prime as unconstraint
-        format.setMaximumFractionDigits(340);
-        configureDecimalFormat(context, format, usedFor);
-        return DecimalFormatEx.of(format, grouping().separator(context, usedFor));
-    }
-
     private static TextEntryParseException parseExceptionForGroupingNotAllowed(final String localeGroupingSeparator, final String input) {
         return new TextEntryParseException("Invalid value '" + input + "'; do not use the '" + localeGroupingSeparator + "' grouping separator");
     }