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;
+ }
}