You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2024/01/16 09:31:03 UTC

(brooklyn-server) branch master updated: better dsl predicate evaluation equality and comparison for numbers

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

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git


The following commit(s) were added to refs/heads/master by this push:
     new b05554acf0 better dsl predicate evaluation equality and comparison for numbers
b05554acf0 is described below

commit b05554acf01529dfa3e0346e1416b6c63bf0dec7
Author: Alex Heneveld <al...@cloudsoft.io>
AuthorDate: Tue Jan 16 09:28:03 2024 +0000

    better dsl predicate evaluation equality and comparison for numbers
    
    fixes equality of different numeric types, and equality of string double with integer
    (both of which should be true)
---
 .../util/core/predicates/DslPredicates.java        | 11 +++--
 .../util/core/predicates/DslPredicateTest.java     | 49 +++++++++++++++++++---
 .../org/apache/brooklyn/util/math/NumberMath.java  |  8 ++--
 3 files changed, 55 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
index 574504cdad..76efad2c9b 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
@@ -61,6 +61,7 @@ import org.apache.brooklyn.util.guava.SerializablePredicate;
 import org.apache.brooklyn.util.javalang.Boxing;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.javalang.coerce.TryCoercer;
+import org.apache.brooklyn.util.math.NumberMath;
 import org.apache.brooklyn.util.text.NaturalOrderComparator;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.text.WildcardGlobs;
@@ -161,13 +162,17 @@ public class DslPredicates {
                 if (ma.equals(((Class<?>) mb).getName()) || ma.equals(((Class<?>) mb).getSimpleName())) return Maybe.of(true);
 
             } else if ((isJson(ma) && !isJson(mb)) || (ma instanceof String && Boxing.isPrimitiveOrBoxedClass(mb.getClass()))) {
-                Maybe<? extends Object> mma = TypeCoercions.tryCoerce(ma, mb.getClass());
+                Class<?> clazz = mb instanceof Number ? Number.class : mb.getClass();
+                Maybe<? extends Object> mma = TypeCoercions.tryCoerce(ma, clazz);
                 if (mma.isPresent()) {
-                    // repeat equality check
-                    if (Objects.equals(mma.get(), mb) || Objects.equals(mb, mma.get())) return Maybe.of(true);
+//                    // repeat equality check
+                    return Maybe.of(coercedEqual(mma.get(), mb));
                 }
                 return Maybe.absent("coercion not supported in equality check, to "+mb.getClass());
+            } else if (a instanceof Number && b instanceof Number) {
+                return Maybe.of(new NumberMath((Number) a).withinTolerance((Number) b));
             }
+
             return Maybe.absent("coercion not permitted for equality check with these argument types");
         };
         return maybeCoercedEquals.apply(a, b)
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/predicates/DslPredicateTest.java b/core/src/test/java/org/apache/brooklyn/util/core/predicates/DslPredicateTest.java
index 61d5ca893c..016059ebbd 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/predicates/DslPredicateTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/predicates/DslPredicateTest.java
@@ -25,6 +25,7 @@ import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.predicates.DslPredicates.DslPredicate;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
 import org.testng.annotations.Test;
@@ -222,12 +223,48 @@ public class DslPredicateTest extends BrooklynMgmtUnitTestSupport {
 
     @Test
     public void testGreaterThanOrEquals() {
-        DslPredicates.DslPredicate p = TypeCoercions.coerce(MutableMap.of(
-                "greater-than-or-equal-to", "5"), DslPredicates.DslPredicate.class);
-        Asserts.assertFalse(p.test("4"));
-        Asserts.assertFalse(p.test("-0.2"));
-        Asserts.assertTrue(p.test("10"));
-        Asserts.assertTrue(p.test("5"));
+        Consumer<Object> check = (v) -> {
+            DslPredicate p = TypeCoercions.coerce(MutableMap.of("greater-than-or-equal-to", v), DslPredicate.class);
+            // numbers always compare as numbers
+            Asserts.assertTrue(p.test(5));
+            Asserts.assertTrue(p.test(5.0));
+            Asserts.assertTrue(p.test(5.1));
+            Asserts.assertFalse(p.test(4.9));
+
+            // strings compare with numbers as numbers, with other strings using NaturalOrder --
+            // so signed or unsigned digit sequences are compared numerically, but decimal parts not
+            Asserts.assertFalse(p.test("4"));
+            Asserts.assertFalse(p.test("-0.2"));
+            Asserts.assertTrue(p.test("10"));
+            Asserts.assertTrue(p.test("09"));
+            Asserts.assertFalse(p.test("04"));
+            Asserts.assertEquals(p.test("5"), !"5.0".equals(v));
+            Asserts.assertEquals(p.test("5.0"), true);
+        };
+        check.accept("5");
+        check.accept("5.0");
+        check.accept(5);
+        check.accept(5.0d);
+    }
+
+    @Test
+    public void testEqualsNumber() {
+        Consumer<Object> check = (v) -> {
+            DslPredicate p = TypeCoercions.coerce(MutableMap.of("equals", v), DslPredicate.class);
+            Asserts.assertFalse(p.test("4"));
+            Asserts.assertFalse(p.test("-0.2"));
+            Asserts.assertFalse(p.test("10"));
+            Asserts.assertEquals(p.test("5"), !"5.0".equals(v));
+            Asserts.assertEquals(p.test("5.0"), !"5".equals(v));
+            Asserts.assertTrue(p.test(5));
+            Asserts.assertTrue(p.test(5.0));
+            Asserts.assertFalse(p.test(5.1));
+            Asserts.assertFalse(p.test(4.9));
+        };
+        check.accept(5);
+        check.accept(5.0d);
+        check.accept("5");
+        check.accept("5.0");
     }
 
     @Test
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/math/NumberMath.java b/utils/common/src/main/java/org/apache/brooklyn/util/math/NumberMath.java
index 023506fc66..eb2b2f2d97 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/math/NumberMath.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/math/NumberMath.java
@@ -56,7 +56,7 @@ public class NumberMath<T extends Number> {
     public Optional<BigInteger> asBigIntegerWithinTolerance() { return asBigIntegerWithinTolerance(number); }
 
     public <T extends Number> T asTypeForced(Class<T> desiredType) {
-        return asTypeFirstMatching(number, desiredType, y -> withinTolerance(number, y));
+        return asTypeFirstMatching(number, desiredType, y -> withinTolerance(y));
     }
     public <T extends Number> Optional<T> asTypeWithinTolerance(Class<T> desiredType, Number tolerance) {
         return Optional.ofNullable(asTypeFirstMatching(number, desiredType, y -> withinTolerance(number, y, tolerance)));
@@ -84,7 +84,7 @@ public class NumberMath<T extends Number> {
 
     public Optional<BigInteger> asBigIntegerWithinTolerance(Number number) {
         BigInteger candidate = asBigIntegerForced(number);
-        if (withinTolerance(number, candidate)) return Optional.of(candidate);
+        if (withinTolerance(candidate)) return Optional.of(candidate);
         return Optional.empty();
     }
 
@@ -112,8 +112,8 @@ public class NumberMath<T extends Number> {
         return null;
     }
 
-    public boolean withinTolerance(Number a, Number b) {
-        return withinTolerance(a, b, tolerance);
+    public boolean withinTolerance(Number b) {
+        return withinTolerance(number, b, tolerance);
     }
     public static boolean withinTolerance(Number a, Number b, Number tolerance) {
         return asBigDecimal(a).subtract(asBigDecimal(b)).abs().compareTo(asBigDecimal(tolerance)) <= 0;