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

commit 3ccc15099258153fb1b30983c8ded4bfdcd0d808
Author: Alex Heneveld <[email protected]>
AuthorDate: Fri Aug 19 13:43:14 2022 +0100

    support key and index in predicate DSL (and check)
---
 .../util/core/predicates/DslPredicates.java        | 135 +++++++++++++--------
 .../util/core/predicates/DslPredicateTest.java     |  53 +++-----
 2 files changed, 100 insertions(+), 88 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 43074054a5..4756bd50a2 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
@@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.util.TokenBuffer;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.reflect.TypeToken;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.objs.BrooklynObject;
@@ -38,9 +39,11 @@ import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import 
org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils;
 import 
org.apache.brooklyn.core.resolve.jackson.JsonSymbolDependentDeserializer;
+import org.apache.brooklyn.core.resolve.jackson.WrappedValue;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.JavaGroovyEquivalents;
 import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.BrooklynTypeNameResolution;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
@@ -63,6 +66,7 @@ import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 public class DslPredicates {
 
@@ -85,8 +89,13 @@ public class DslPredicates {
     }
 
     public enum WhenPresencePredicate {
-        /** value cannot be resolved (not even as null) */ ABSENT,
-        /** value is null or cannot be resolved */ ABSENT_OR_NULL,
+        // we might want these, but it is tricky sometimes even in our code to 
distinguish!
+//        /** value is set but unresolvable (unset or not immediately 
resolvable) */ UNRESOLVABLE,
+//        /** value is not set (including set to something not resolvable or 
not immediately resolvable) */ UNSET,
+//        /** value is set to an explicit null */ NULL,
+
+        /** value unavailable (unset or not immediately resolvable) */ ABSENT,
+        /** value is null or unavailable */ ABSENT_OR_NULL,
         /** value is available, but might be null */ PRESENT,
         /** value is available and non-null (but might be 0 or empty) */ 
PRESENT_NON_NULL,
         /** value is available and ready/truthy (eg not false or empty) */ 
TRUTHY,
@@ -176,12 +185,15 @@ public class DslPredicates {
         public String regex;
         public String glob;
 
+        /** nested check */
+        public DslPredicate check;
         public List<DslPredicate> any;
         public List<DslPredicate> all;
 
         public @JsonProperty("has-element") DslPredicate hasElement;
-//        public @JsonProperty("at-key") DslPredicate containsKey;
-//        public @JsonProperty("at-index") DslPredicate containsValue;
+        public Object key;
+        public Integer index;
+        public String jsonpath;
 
         public WhenPresencePredicate when;
 
@@ -236,8 +248,45 @@ public class DslPredicates {
             return applyToResolved(result);
         }
 
+        protected void 
collectApplicableSpecialFieldTargetResolvers(Map<String,Function<Object,Maybe<Object>>>
 resolvers) {
+            if (key!=null) resolvers.put("key", (value) -> {
+                if (value instanceof Map) {
+                    if (((Map) value).containsKey(key)) {
+                        return Maybe.ofAllowingNull( ((Map) value).get(key) );
+                    } else {
+                        return Maybe.absent("Cannot find indicated key in 
map");
+                    }
+                } else {
+                    return Maybe.absent("Cannot evaluate key on non-map 
target");
+                }
+            });
+
+            if (index != null) resolvers.put("index", (value) -> {
+                Integer i = index;
+                if (value instanceof Iterable) {
+                    int size = Iterables.size((Iterable) value);
+                    if (i<0) {
+                        i = size + i;
+                    }
+                    if (i<0 || i>=size) return Maybe.absent("No element at 
index "+i+"; size "+size);
+                    return Maybe.of(Iterables.get((Iterable)value, i));
+                } else {
+                    return Maybe.absent("Cannot evaluate index on non-list 
target");
+                }
+            });
+        }
+
         protected Maybe<Object> resolveTargetAgainstInput(Object input) {
-            return Maybe.ofAllowingNull(input);
+            Map<String,Function<Object,Maybe<Object>>> specialResolvers = 
MutableMap.of();
+            collectApplicableSpecialFieldTargetResolvers(specialResolvers);
+
+            if (!specialResolvers.isEmpty()) {
+                if (specialResolvers.size()>1) throw new 
IllegalStateException("Predicate has multiple incompatible target specifiers: 
"+specialResolvers.keySet());
+                return 
specialResolvers.values().iterator().next().apply(input);
+
+            } else {
+                return Maybe.ofAllowingNull(input);
+            }
         }
 
         public boolean applyToResolved(Maybe<Object> result) {
@@ -279,22 +328,7 @@ public class DslPredicates {
                 return false;
             });
 
-            // TODO at-key, at-value; preserver order
-//            checker.check(element, test -> nestedPredicateCheck(test, 
result));
-//
-//            if (result.isPresent() && result.get() instanceof Iterable) {
-//                // iterate through lists
-//                for (Object v : ((Iterable) result.get())) {
-//                    CheckCounts checker2 = new CheckCounts();
-//                    applyToResolved(v instanceof Maybe ? (Maybe) v : 
Maybe.of(v), checker2);
-//                    if (checker2.allPassed(true)) {
-//                        checker.add(checker2);
-//                        return;
-//                    }
-//                }
-//                // did not pass when flattened; try with unflattened
-//            }
-
+            checker.check(check, test -> nestedPredicateCheck(check, result));
             checker.check(any, test -> test.stream().anyMatch(p -> 
nestedPredicateCheck(p, result)));
             checker.check(all, test -> test.stream().allMatch(p -> 
nestedPredicateCheck(p, result)));
 
@@ -336,7 +370,7 @@ public class DslPredicates {
                     ? p.apply(result.get())
                     : p instanceof DslPredicateBase
                     // in case it does a when: absent check
-                    ? ((DslEntityPredicateDefault)p).applyToResolved(result)
+                    ? ((DslPredicateBase)p).applyToResolved(result)
                     : false;
         }
     }
@@ -363,6 +397,31 @@ public class DslPredicates {
         public String sensor;
         public DslPredicate tag;
 
+        protected void 
collectApplicableSpecialFieldTargetResolvers(Map<String,Function<Object, 
Maybe<Object>>> resolvers) {
+            super.collectApplicableSpecialFieldTargetResolvers(resolvers);
+
+            if (config!=null) resolvers.put("config", (value) -> {
+                if (value instanceof Configurable) {
+                    ValueResolver<Object> resolver = 
Tasks.resolving((DeferredSupplier) () -> 
((Configurable)value).config().get(ConfigKeys.newConfigKey(Object.class, 
config)))
+                            
.as(Object.class).allowDeepResolution(true).immediately(true);
+                    if (value instanceof Entity) resolver.context( 
(Entity)value );
+                    return resolver.getMaybe();
+                } else {
+                    return Maybe.absent("Config not supported on " + value + " 
(testing config '" + config + "')");
+                }
+            });
+
+            if (sensor!=null) resolvers.put("sensor", (value) -> {
+                if (value instanceof Entity) {
+                    ValueResolver<Object> resolver = 
Tasks.resolving((DeferredSupplier) () -> 
((Entity)value).sensors().get(Sensors.newSensor(Object.class, sensor)))
+                            
.as(Object.class).allowDeepResolution(true).immediately(true);
+                    return resolver.getMaybe();
+                } else {
+                    return Maybe.absent("Sensors not supported on " + value + 
" (testing sensor '" + config + "')");
+                }
+            });
+        }
+
         protected Maybe<Object> resolveTargetAgainstInput(Object input) {
             Object target = this.target;
             Maybe<Object> result;
@@ -377,37 +436,7 @@ public class DslPredicates {
                 result = resolver.getMaybe();
             }
 
-            if (config!=null) {
-                if (sensor!=null) {
-                    throw new IllegalStateException("One predicate cannot 
specify to test both config key '"+config+"' and sensor '"+sensor+"'; use 'all' 
with children");
-                }
-
-                if (result.isPresent()) {
-                    Object ro = result.get();
-                    if (ro instanceof Configurable) {
-                        ValueResolver<Object> resolver = 
Tasks.resolving((DeferredSupplier) () -> 
((Configurable)ro).config().get(ConfigKeys.newConfigKey(Object.class, config)))
-                                
.as(Object.class).allowDeepResolution(true).immediately(true);
-                        if (ro instanceof Entity) resolver.context( (Entity)ro 
);
-                        result = resolver.getMaybe();
-                    } else {
-                        result = Maybe.absent("Config not supported on " + ro 
+ " (testing config '" + config + "')");
-                    }
-                }
-            }
-
-            if (sensor!=null) {
-                if (result.isPresent()) {
-                    Object ro = result.get();
-                    if (ro instanceof Entity) {
-                        ValueResolver<Object> resolver = 
Tasks.resolving((DeferredSupplier) () -> 
((Entity)ro).sensors().get(Sensors.newSensor(Object.class, sensor)))
-                                
.as(Object.class).allowDeepResolution(true).immediately(true);
-                        result = resolver.getMaybe();
-                    } else {
-                        result = Maybe.absent("Sensors not supported on " + ro 
+ " (testing sensor '" + config + "')");
-                    }
-                }
-            }
-
+            result = result.isPresent() ? 
super.resolveTargetAgainstInput(result.get()) : result;
             return result;
         }
 
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 ba06b7e4ef..779970b50a 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
@@ -215,41 +215,6 @@ public class DslPredicateTest extends 
BrooklynMgmtUnitTestSupport {
         }
     }
 
-    // auto-unflattening was too weird; instead prompt for "element", also 
"map-key", "map-value"
-//    @Test
-//    public void testAllWithListWithVariousFlattening() {
-//        Asserts.assertTrue(SetAllowingEqualsToList.of(MutableSet.of("y", 
"x")).equals(MutableList.of("x", "y")));
-//
-//        DslPredicates.DslPredicate p = 
TypeCoercions.coerce(MutableMap.of("equals", MutableList.of("x", "y")), 
DslPredicates.DslPredicate.class);
-//        Asserts.assertTrue(p.test(Arrays.asList("x", "y")));
-//        // list not equal because of order and equal
-//        Asserts.assertFalse(p.test(Arrays.asList("y", "x")));
-//        Asserts.assertFalse(p.test(Arrays.asList("x", "y", "z")));
-//        // set equality _does_ match without order
-//        
Asserts.assertTrue(p.test(SetAllowingEqualsToList.of(MutableSet.of("y", "x"))));
-//
-//        // "all" works because it attempts unflattened at all, then flattens 
on each test
-//        p = TypeCoercions.coerce(MutableMap.of("all", MutableList.of("x", 
"y")), DslPredicates.DslPredicate.class);
-//        Asserts.assertTrue(p.test(Arrays.asList("y", "x")));
-//        Asserts.assertTrue(p.test(Arrays.asList("x", "y", "z")));
-//        // set equality _does_ match!
-//        
Asserts.assertTrue(p.test(SetAllowingEqualsToList.of(MutableSet.of("y", "x"))));
-//
-//        // specify unflattening also works, but is unnecessary
-//        p = TypeCoercions.coerce(MutableMap.of("unflattened", 
MutableMap.of("all", MutableList.of("x", "y"))), 
DslPredicates.DslPredicate.class);
-//        Asserts.assertTrue(p.test(Arrays.asList("x", "y", "z")));
-//
-//        p = TypeCoercions.coerce(MutableMap.of("unflattened", 
MutableMap.of("equals", MutableList.of("x", "y"))), 
DslPredicates.DslPredicate.class);
-//        Asserts.assertFalse(p.test(Arrays.asList("x", "y", "z")));
-//        Asserts.assertTrue(p.test(Arrays.asList("x", "y")));
-//
-//        p = TypeCoercions.coerce("x", DslPredicates.DslPredicate.class);
-//        Asserts.assertTrue(p.test(Arrays.asList("x", "y", "z")));
-//
-////        DslPredicates.DslPredicate
-//                p = TypeCoercions.coerce(MutableMap.of("unflattened", "x"), 
DslPredicates.DslPredicate.class);
-//        Asserts.assertFalse(p.test(Arrays.asList("x", "y", "z")));
-//    }
     @Test
     public void testListsAndElement() {
         Asserts.assertTrue(SetAllowingEqualsToList.of(MutableSet.of("y", 
"x")).equals(MutableList.of("x", "y")));
@@ -289,6 +254,24 @@ public class DslPredicateTest extends 
BrooklynMgmtUnitTestSupport {
         Asserts.assertFalse(p.test(Arrays.asList("xx", "yy")));
     }
 
+    @Test
+    public void testKeyAndAtIndex() {
+        DslPredicates.DslPredicate p = 
TypeCoercions.coerce(MutableMap.of("key", "name", "regex", "[Bb].*"), 
DslPredicates.DslPredicate.class);
+        Asserts.assertTrue(p.test(MutableMap.of("id", 123, "name", "Bob")));
+        Asserts.assertFalse(p.test(MutableMap.of("id", 124, "name", 
"Astrid")));
+
+        p = TypeCoercions.coerce(MutableMap.of("index", -1, "regex", 
"[Bb].*"), DslPredicates.DslPredicate.class);
+        Asserts.assertTrue(p.test(MutableList.of("Astrid", "Bob")));
+        Asserts.assertFalse(p.test(MutableList.of("Astrid", "Bob", "Carver")));
+
+        // nested check
+        p = TypeCoercions.coerce(MutableMap.of("index", 1,
+                "check", MutableMap.of("key", "name", "regex", "[Bb].*")), 
DslPredicates.DslPredicate.class);
+        Asserts.assertFalse(p.test(MutableList.of(MutableMap.of("name", 
"Bob"))));
+        Asserts.assertTrue(p.test(MutableList.of("Astrid", 
MutableMap.of("name", "Bob"))));
+        Asserts.assertFalse(p.test(MutableList.of("Astrid", 
MutableMap.of("name", "Carver"))));
+    }
+
     @Test
     public void testLocationTagImplicitEquals() {
         DslPredicates.DslPredicate p = TypeCoercions.coerce(MutableMap.of(

Reply via email to