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

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 27ce7088b0 For an expression such as `equal(function("isNaN", …), 
literal(true))`, simplify to a more direct filter such as `function("isNaN", 
…)`.
27ce7088b0 is described below

commit 27ce7088b0092a2b907f762ec402f1f2e89a8ffa
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Mar 25 12:05:00 2026 +0100

    For an expression such as `equal(function("isNaN", …), literal(true))`,
    simplify to a more direct filter such as `function("isNaN", …)`.
---
 .../org/apache/sis/filter/ComparisonFilter.java    | 72 ++++++++++++++++++++--
 .../main/org/apache/sis/filter/LogicalFilter.java  | 15 ++---
 .../main/org/apache/sis/filter/Optimization.java   |  2 +-
 .../apache/sis/filter/math/FilteringFunction.java  | 62 +++++++++++++++++++
 .../main/org/apache/sis/filter/math/Predicate.java | 28 ++++++++-
 5 files changed, 164 insertions(+), 15 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
index 07a72eba9e..eeeb7a83d8 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
@@ -36,6 +36,7 @@ import org.opengis.filter.MatchAction;
 import org.opengis.filter.ComparisonOperatorName;
 import org.opengis.filter.BinaryComparisonOperator;
 import org.opengis.filter.BetweenComparisonOperator;
+import org.opengis.filter.Literal;
 
 
 /**
@@ -173,6 +174,47 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
     @Override
     public abstract ComparisonFilter<R> recreate(Expression<R,?>[] effective);
 
+    /**
+     * If one of the expressions is a filter compared to a Boolean literal, 
returns the filter directly.
+     * This optimization should be attempted only when the return value of 
both expressions are Boolean.
+     *
+     * @param  optimization  the simplifications or optimizations to apply on 
this filter.
+     * @param  swap          whether to swap the {@code expression1} and 
{@code expression2} order.
+     * @return the filter, or {@code null} if none.
+     */
+    private Filter<R> extractFilter(final Optimization optimization, final 
boolean swap) {
+        Expression<R,?> e = swap ? expression1 : expression2;
+        if (e instanceof Literal<?,?>) {
+            final Boolean literal = (Boolean) ((Literal<?,?>) e).getValue();
+            if (literal == null) {
+                return Filter.exclude();
+            }
+            final int c0, c1;
+            if (swap) {
+                c0 = literal.compareTo(Boolean.FALSE);
+                c1 = literal.compareTo(Boolean.TRUE);
+            } else {
+                c0 = Boolean.FALSE.compareTo(literal);
+                c1 = Boolean.TRUE .compareTo(literal);
+            }
+            final boolean ifFalse = fromCompareTo(c0);
+            final boolean ifTrue  = fromCompareTo(c1);
+            if (ifFalse == ifTrue) {
+                return ifTrue ? Filter.include() : Filter.exclude();
+            }
+            e = swap ? expression2 : expression1;
+            if (e instanceof Filter<?>) {
+                @SuppressWarnings("unchecked")
+                var filter = (Filter<R>) e;
+                if (ifFalse) {
+                    filter = new 
LogicalFilter.Not<>(filter).optimize(optimization);
+                }
+                return filter;
+            }
+        }
+        return null;
+    }
+
     /**
      * Tries to optimize this filter. Fist, this method applies the 
optimization documented
      * in the {@linkplain Optimization.OnFilter#optimize default method 
impmementation}.
@@ -201,6 +243,14 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
                 }
                 if (Comparable.class.isAssignableFrom(t1) && 
t1.isAssignableFrom(t2)) {
                     if (isMatchingCase || 
!CharSequence.class.isAssignableFrom(t1)) {
+                        if (t1 == Boolean.class) {
+                            Filter<R> filter;
+                            if ((filter = extractFilter(optimization, false)) 
!= null ||
+                                (filter = extractFilter(optimization, true))  
!= null)
+                            {
+                                return filter;
+                            }
+                        }
                         return new Comparables();
                     }
                 }
@@ -307,6 +357,11 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
         @Override public    List<Expression<R,?>> getExpressions()   {return 
ComparisonFilter.this.getExpressions();}
         @Override protected Collection<?>         getChildren()      {return 
ComparisonFilter.this.getChildren();}
 
+        /** Creates a new filter of the same type but different parameters. */
+        @Override public Filter<R> recreate(Expression<R,?>[] effective) {
+            return ComparisonFilter.this.recreate(effective).new Comparables();
+        }
+
         /** Determines if the test represented by this filter passes with the 
given operands. */
         @Override public boolean test(final R candidate) {
             final Object left = expression1.apply(candidate);
@@ -322,11 +377,6 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
             }
             return false;
         }
-
-        /** Creates a new filter of the same type but different parameters. */
-        @Override public Filter<R> recreate(Expression<R,?>[] effective) {
-            return ComparisonFilter.this.recreate(effective).new Comparables();
-        }
     }
 
     /**
@@ -406,7 +456,7 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
      *
      * @todo Delegate all comparisons of temporal objects to {@link 
TemporalFilter}.
      */
-    private boolean evaluate(Object left, Object right) {
+    private boolean evaluate(final Object left, final Object right) {
         /*
          * For numbers, the apply(…) method inherited from parent class will 
delegate to specialized methods like
          * applyAsDouble(…). All implementations of those specialized methods 
in ComparisonFilter return integer,
@@ -659,6 +709,11 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
             super(expression1, expression2, isMatchingCase, matchAction);
         }
 
+        /** Creates a new filter with the same expressions as the given 
parent. */
+        EqualTo(final ComparisonFilter<R> parent) {
+            super(parent.expression1, parent.expression2, 
parent.isMatchingCase, parent.matchAction);
+        }
+
         /** Creates a new filter of the same type but different parameters. */
         @Override public ComparisonFilter<R> recreate(final Expression<R,?>[] 
effective) {
             return new EqualTo<>(effective[0], effective[1], isMatchingCase, 
matchAction);
@@ -699,6 +754,11 @@ abstract class ComparisonFilter<R> extends 
BinaryFunctionWidening<R, Object, Obj
             super(expression1, expression2, isMatchingCase, matchAction);
         }
 
+        /** Creates a new filter with the same expressions as the given 
parent. */
+        NotEqualTo(final ComparisonFilter<R> parent) {
+            super(parent.expression1, parent.expression2, 
parent.isMatchingCase, parent.matchAction);
+        }
+
         /** Creates a new filter of the same type but different parameters. */
         @Override public ComparisonFilter<R> recreate(final Expression<R,?>[] 
effective) {
             return new NotEqualTo<>(effective[0], effective[1], 
isMatchingCase, matchAction);
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
index 098307d252..8f52690448 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
@@ -270,13 +270,14 @@ abstract class LogicalFilter<R> extends Node implements 
LogicalOperator<R>, Opti
             if (effective == Filter.include()) return Filter.exclude();
             if (effective == Filter.exclude()) return Filter.include();
             if (effective instanceof Not<?>) {
-                return ((Not<R>) effective).operand;            // NOT(NOT(C)) 
== C
-            } else {
-                /*
-                 * TODO:
-                 * NOT(EQUALS(A,B)) = NOT_EQUALS(A,B)
-                 * NOT(NOT_EQUALS(A,B)) = EQUALS(A,B)
-                 */
+                // NOT(NOT(A)) == A
+                return ((Not<R>) effective).operand;
+            } else if (effective instanceof ComparisonFilter.EqualTo<?>) {
+                // NOT(EQUALS(A,B)) = NOT_EQUALS(A,B)
+                return new 
ComparisonFilter.NotEqualTo<>((ComparisonFilter.EqualTo<R>) effective);
+            } else if (effective instanceof ComparisonFilter.NotEqualTo<?>) {
+                // NOT(NOT_EQUALS(A,B)) = EQUALS(A,B)
+                return new 
ComparisonFilter.EqualTo<>((ComparisonFilter.NotEqualTo<R>) effective);
             }
             return (effective != operand) ? new Not<>(effective) : this;
         }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
index 453785a2eb..09cd697b0d 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
@@ -388,7 +388,7 @@ public class Optimization {
     }
 
     /**
-     * Converts all literal to the same class as result of non-literal 
parameters.
+     * Converts all literals to the same class as the result of non-literal 
parameters.
      * This is useful for example in {@link ComparisonFilter} for avoiding to 
perform
      * the same conversion of literal value each time that the filter is 
executed.
      *
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/FilteringFunction.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/FilteringFunction.java
new file mode 100644
index 0000000000..4129641998
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/FilteringFunction.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.filter.math;
+
+import org.opengis.util.CodeList;
+
+
+/**
+ * A function viewed as a code list.
+ * This is used only when {@link Predicate} is used as a filter.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+final class FilteringFunction extends CodeList<FilteringFunction> {
+    /**
+     * Serial number for compatibility with different versions.
+     */
+    private static final long serialVersionUID = -3980988422378881835L;
+
+    /**
+     * Creates a new filtering function.
+     *
+     * @param  name  name of the function.
+     */
+    private FilteringFunction(final String name) {
+        super(name);
+    }
+
+    /**
+     * Returns the list of {@code FilteringFunction}s.
+     *
+     * @return the list of codes declared in the current JVM.
+     */
+    @Override
+    public FilteringFunction[] family() {
+        return values(FilteringFunction.class);
+    }
+
+    /**
+     * Returns the function name that matches the given string, or a new one 
if none match it.
+     *
+     * @param  code  the name of the code to fetch or to create.
+     * @return a code matching the given name, or {@code null}.
+     */
+    public static FilteringFunction valueOf(final String code) {
+        return valueOf(FilteringFunction.class, code, 
FilteringFunction::new).get();
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Predicate.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Predicate.java
index 6b0e27f403..2426fc031b 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Predicate.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Predicate.java
@@ -19,6 +19,7 @@ package org.apache.sis.filter.math;
 import java.util.Objects;
 import java.util.function.DoublePredicate;
 import java.io.ObjectStreamException;
+import org.opengis.util.CodeList;
 import org.opengis.util.ScopedName;
 import org.apache.sis.filter.Optimization;
 import org.apache.sis.filter.base.UnaryFunction;
@@ -27,17 +28,22 @@ import 
org.apache.sis.feature.internal.shared.FeatureProjectionBuilder;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.filter.Expression;
+import org.opengis.filter.Filter;
 
 
 /**
  * An operation on a single operand and returning a Boolean value.
+ * Can also be used as a filter for avoiding the need to use an
+ * expression such as {@code equals(predicate, literal(true))}.
  *
  * @author  Martin Desruisseaux (Geomatys)
  *
  * @param  <R>  the type of resources (e.g. {@link 
org.opengis.feature.Feature}) used as inputs.
  */
 final class Predicate<R> extends UnaryFunction<R, Number>
-        implements FeatureExpression<R, Boolean>, Optimization.OnExpression<R, 
Boolean>
+        implements Optimization.OnExpression<R, Boolean>,
+                   FeatureExpression<R, Boolean>,
+                   Filter<R>
 {
     /**
      * For cross-version compatibility.
@@ -78,6 +84,14 @@ final class Predicate<R> extends UnaryFunction<R, Number>
         return function.getFunctionName();
     }
 
+    /**
+     * Returns the nature of the operator.
+     */
+    @Override
+    public CodeList<?> getOperatorType() {
+        return FilteringFunction.valueOf(function.name());
+    }
+
     /**
      * Returns the type of values computed by this expression.
      */
@@ -105,4 +119,16 @@ final class Predicate<R> extends UnaryFunction<R, Number>
         }
         return null;
     }
+
+    /**
+     * Given an object, determines if the test(s) represented by this filter 
are passed.
+     */
+    @Override
+    public boolean test(final R feature) {
+        final Number value = expression.apply(feature);
+        if (value != null) {
+            return math.test(value.doubleValue());
+        }
+        return false;
+    }
 }

Reply via email to