You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/05/01 15:27:04 UTC

[sis] branch geoapi-4.0 updated (92777dd94e -> cb0796b495)

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

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


    from 92777dd94e Bug fix in the computation of image bounds by `ImageCombiner`. Workaround for `BufferedImage.setData(…)` bug, which does not handle float and double types correctly.
     new 26a0f263d2 Implement the `getResourceClass()` method added in `Filter` and `Expression` interfaces. That method make possible to determine whether some optimizations are allowed or not.
     new cb0796b495 Remove the `FilterNode` class, replaced by default implementation of `Optimization.OnFilter` methods.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/sis/feature/FeatureOperations.java  |   2 +-
 .../org/apache/sis/filter/AssociationValue.java    |  10 +-
 .../java/org/apache/sis/filter/BinaryFunction.java |  11 +++
 .../apache/sis/filter/BinaryGeometryFilter.java    |  14 ++-
 .../org/apache/sis/filter/ComparisonFilter.java    |  19 +++-
 .../apache/sis/filter/DefaultFilterFactory.java    |   4 +-
 .../java/org/apache/sis/filter/FilterNode.java     | 105 ---------------------
 .../org/apache/sis/filter/IdentifierFilter.java    |  30 ++++--
 .../java/org/apache/sis/filter/LikeFilter.java     |  11 ++-
 .../java/org/apache/sis/filter/LogicalFilter.java  |  22 ++++-
 .../java/org/apache/sis/filter/Optimization.java   |  33 +++++--
 .../java/org/apache/sis/filter/PropertyValue.java  |  12 ++-
 .../java/org/apache/sis/filter/UnaryFunction.java  |  10 ++
 .../sis/internal/coverage/j2d/ObservableImage.java |   2 +-
 .../sis/internal/filter/GeometryConverter.java     |   8 ++
 .../java/org/apache/sis/internal/filter/Node.java  |  22 ++++-
 .../internal/filter/sqlmm/FunctionWithSRID.java    |   9 ++
 .../internal/filter/sqlmm/GeometryConstructor.java |   8 ++
 .../sis/internal/filter/sqlmm/OneGeometry.java     |  18 +++-
 .../apache/sis/internal/filter/sqlmm/ST_Point.java |  12 +++
 .../sis/internal/filter/sqlmm/ST_Transform.java    |   8 ++
 .../sis/internal/filter/sqlmm/TwoGeometries.java   |  16 ++++
 .../apache/sis/filter/IdentifierFilterTest.java    |   4 +-
 .../org/apache/sis/filter/LeafExpressionTest.java  |   3 +-
 .../org/apache/sis/filter/LogicalFilterTest.java   |  21 ++++-
 .../apache/sis/internal/filter/FunctionMock.java   |   8 ++
 .../sis/internal/filter/FunctionNamesTest.java     |   2 +
 .../sis/internal/filter/ValueReferenceMock.java    |   8 ++
 28 files changed, 296 insertions(+), 136 deletions(-)
 delete mode 100644 core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java


[sis] 01/02: Implement the `getResourceClass()` method added in `Filter` and `Expression` interfaces. That method make possible to determine whether some optimizations are allowed or not.

Posted by de...@apache.org.
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

commit 26a0f263d22459f766629a67e3c382a37dfe5868
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon May 1 13:38:39 2023 +0200

    Implement the `getResourceClass()` method added in `Filter` and `Expression` interfaces.
    That method make possible to determine whether some optimizations are allowed or not.
---
 .../org/apache/sis/feature/FeatureOperations.java  |  2 +-
 .../org/apache/sis/filter/AssociationValue.java    | 10 +++++++++-
 .../java/org/apache/sis/filter/BinaryFunction.java | 11 +++++++++++
 .../apache/sis/filter/BinaryGeometryFilter.java    | 11 +++++++++++
 .../org/apache/sis/filter/ComparisonFilter.java    |  6 ++++++
 .../apache/sis/filter/DefaultFilterFactory.java    |  4 ++--
 .../java/org/apache/sis/filter/FilterNode.java     | 13 +++++++++++--
 .../org/apache/sis/filter/IdentifierFilter.java    | 19 ++++++++++++-------
 .../java/org/apache/sis/filter/LikeFilter.java     |  8 ++++++++
 .../java/org/apache/sis/filter/LogicalFilter.java  | 17 +++++++++++++++++
 .../java/org/apache/sis/filter/Optimization.java   |  8 ++++----
 .../java/org/apache/sis/filter/PropertyValue.java  | 12 ++++++++++--
 .../java/org/apache/sis/filter/UnaryFunction.java  | 10 ++++++++++
 .../sis/internal/coverage/j2d/ObservableImage.java |  2 +-
 .../sis/internal/filter/GeometryConverter.java     |  8 ++++++++
 .../java/org/apache/sis/internal/filter/Node.java  | 22 +++++++++++++++++++++-
 .../internal/filter/sqlmm/FunctionWithSRID.java    |  9 +++++++++
 .../internal/filter/sqlmm/GeometryConstructor.java |  8 ++++++++
 .../sis/internal/filter/sqlmm/OneGeometry.java     | 18 +++++++++++++++++-
 .../apache/sis/internal/filter/sqlmm/ST_Point.java | 12 ++++++++++++
 .../sis/internal/filter/sqlmm/ST_Transform.java    |  8 ++++++++
 .../sis/internal/filter/sqlmm/TwoGeometries.java   | 16 ++++++++++++++++
 .../apache/sis/filter/IdentifierFilterTest.java    |  4 +++-
 .../org/apache/sis/filter/LeafExpressionTest.java  |  3 ++-
 .../apache/sis/internal/filter/FunctionMock.java   |  8 ++++++++
 .../sis/internal/filter/FunctionNamesTest.java     |  2 ++
 .../sis/internal/filter/ValueReferenceMock.java    |  8 ++++++++
 27 files changed, 235 insertions(+), 24 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java b/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
index b9ab137a05..b4ab1692d0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
@@ -290,7 +290,7 @@ public final class FeatureOperations extends Static {
     /**
      * Creates an operation which delegates the computation to a given expression producing values of unknown type.
      * This method can be used as an alternative to {@link #expression expression(…)} when the constraint on the
-     * parameterized type {@code <V>} between {@code expression} and {@code result} can not be enforced at compile time.
+     * parameterized type {@code <V>} between {@code expression} and {@code result} cannot be enforced at compile time.
      * This method casts or converts the expression to the expected type by a call to
      * {@link Expression#toValueType(Class)}.
      *
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/AssociationValue.java b/core/sis-feature/src/main/java/org/apache/sis/filter/AssociationValue.java
index 16d967535c..c8d714b8d5 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/AssociationValue.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/AssociationValue.java
@@ -42,7 +42,7 @@ import org.opengis.filter.ValueReference;
  * (the tip) is evaluated by a {@link PropertyValue}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  *
  * @param  <V>  the type of value computed by the expression.
  *
@@ -94,6 +94,14 @@ final class AssociationValue<V> extends LeafExpression<Feature, V>
         this.accessor = accessor;
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public final Class<Feature> getResourceClass() {
+        return Feature.class;
+    }
+
     /**
      * For {@link #toString()} implementation.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryFunction.java b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryFunction.java
index b7c33e33a2..a149e05cf9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryFunction.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryFunction.java
@@ -84,6 +84,17 @@ abstract class BinaryFunction<R,V1,V2> extends Node {
         this.expression2 = expression2;
     }
 
+    /**
+     * Returns the class of resources expected by this filter.
+     * Defined for {@link Filter#getResourceClass()} and {@link Expression#getResourceClass()} implementations.
+     *
+     * @return type of resources accepted by this filter, or {@code null} if inconsistent.
+     */
+    public final Class<? super R> getResourceClass() {
+        return specializedClass(expression1.getResourceClass(),
+                                expression2.getResourceClass());
+    }
+
     /**
      * Returns the expressions used as parameters by this function.
      * Defined for {@link Expression#getParameters()} implementations.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
index f668f78886..8b62b37a7f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
@@ -164,6 +164,17 @@ abstract class BinaryGeometryFilter<R,G> extends FilterNode<R> implements Spatia
         return unwrapped;
     }
 
+    /**
+     * Returns the class of resources expected by this filter.
+     *
+     * @return type of resources accepted by this filter, or {@code null} if inconsistent.
+     */
+    @Override
+    public final Class<? super R> getResourceClass() {
+        return specializedClass(expression1.getResourceClass(),
+                                expression2.getResourceClass());
+    }
+
     /**
      * Returns the two expressions used as parameters by this filter.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
index 1853662fe7..791e545b61 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
@@ -812,6 +812,12 @@ abstract class ComparisonFilter<R> extends BinaryFunction<R,Object,Object>
             this.upper = new    LessThanOrEqualTo<>(expression, upper, true, MatchAction.ANY);
         }
 
+        /** Returns the class of resources expected by this filter. */
+        @Override public final Class<? super R> getResourceClass() {
+            return specializedClass(lower.getResourceClass(),
+                                    upper.getResourceClass());
+        }
+
         /**
          * Returns the 3 children of this node. Since {@code lower.expression2}
          * is the same as {@code upper.expression1}, that repetition is omitted.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
index 5f381105b0..7d56e36cc1 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
@@ -186,7 +186,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends AbstractFactory implem
          */
         @Override
         public ResourceId<Feature> resourceId(final String identifier) {
-            return new IdentifierFilter<>(identifier);
+            return new IdentifierFilter(identifier);
         }
 
         /**
@@ -207,7 +207,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends AbstractFactory implem
         public ResourceId<Feature> resourceId(final String identifier, final Version version,
                                               final Instant startTime, final Instant endTime)
         {
-            return new IdentifierFilter<>(identifier);
+            return new IdentifierFilter(identifier);
         }
 
         /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java b/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java
index d5a8c25b85..d35ad68a46 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java
@@ -58,10 +58,19 @@ abstract class FilterNode<R> extends Node implements Filter<R> {
      * @param  <R>       desired parameterized type.
      * @param  template  the filter from which to get the runtime value of {@code <R>}.
      * @param  other     the predicate to cast to a filter compatible with the target.
-     * @return the casted predicate, or {@code null} if it can not be casted.
+     * @return the casted predicate, or {@code null} if it cannot be casted.
      */
+    @SuppressWarnings("unchecked")
     static <R> Filter<R> castOrNull(final Filter<R> template, final Predicate<? super R> other) {
-        // TODO
+        if (other instanceof Filter<?>) {
+            final Class<?> type = template.getResourceClass();
+            if (type != null) {
+                final Class<?> to = ((Filter<?>) other).getResourceClass();
+                if (to != null && type.isAssignableFrom(to)) {
+                    return (Filter<R>) other;
+                }
+            }
+        }
         return null;
     }
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
index fbfe274224..3e2ec855a0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
@@ -34,12 +34,9 @@ import org.opengis.filter.ResourceId;
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.4
- *
- * @param  <R>  the type of resources used as inputs.
- *
- * @since 1.1
+ * @since   1.1
  */
-final class IdentifierFilter<R extends Feature> extends FilterNode<R> implements ResourceId<R> {
+final class IdentifierFilter extends FilterNode<Feature> implements ResourceId<Feature> {
     /**
      * For cross-version compatibility.
      */
@@ -58,6 +55,14 @@ final class IdentifierFilter<R extends Feature> extends FilterNode<R> implements
         this.identifier = identifier;
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<Feature> getResourceClass() {
+        return Feature.class;
+    }
+
     /**
      * Returns the identifiers of feature instances to accept.
      */
@@ -70,7 +75,7 @@ final class IdentifierFilter<R extends Feature> extends FilterNode<R> implements
      * Returns the parameters of this filter.
      */
     @Override
-    public List<Expression<R,?>> getExpressions() {
+    public List<Expression<Feature,?>> getExpressions() {
         return List.of(new LeafExpression.Literal<>(identifier));
     }
 
@@ -88,7 +93,7 @@ final class IdentifierFilter<R extends Feature> extends FilterNode<R> implements
      * is one of the identifier specified at {@code IdentifierFilter} construction time.
      */
     @Override
-    public boolean test(R object) {
+    public boolean test(final Feature object) {
         if (object == null) {
             return false;
         }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
index f4cb6bbc7d..d2651378be 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
@@ -161,6 +161,14 @@ final class LikeFilter<R> extends FilterNode<R> implements LikeOperator<R>, Opti
         return new LikeFilter<>(this, effective[0]);
     }
 
+    /**
+     * Returns the class of resources expected by this filter.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return expression.getResourceClass();
+    }
+
     /**
      * Returns the children of this node for displaying purposes.
      * This is used by {@link #toString()}, {@link #hashCode()} and {@link #equals(Object)} implementations.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
index 4dee42760d..0bdde0b4e6 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
@@ -86,6 +86,18 @@ abstract class LogicalFilter<R> extends FilterNode<R> implements LogicalOperator
      */
     protected abstract LogicalFilter<R> createSameType(Collection<? extends Filter<R>> op);
 
+    /**
+     * Returns the class of resources expected by this filter.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        Class<? super R> type = Object.class;
+        for (final Filter<R> operand : operands) {
+            type = specializedClass(type, operand.getResourceClass());
+        }
+        return type;
+    }
+
     /**
      * Returns a list containing all of the child filters of this object.
      */
@@ -230,6 +242,11 @@ abstract class LogicalFilter<R> extends FilterNode<R> implements LogicalOperator
             return LogicalOperatorName.NOT;
         }
 
+        /** Returns the class of resources expected by this filter. */
+        @Override public Class<? super R> getResourceClass() {
+            return operand.getResourceClass();
+        }
+
         /** Symbol of the operation. */
         @Override protected char symbol() {
             return '¬';
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java b/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
index d13ef64099..2f1ca09039 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
@@ -233,8 +233,8 @@ public class Optimization {
 
         /**
          * Returns the {@code AND} logical operation between this filter and the given predicate.
-         * If the given predicate is an instance of {@link Filter}, then the returned predicate
-         * is an instance of {@code Optimization.OnFilter}.
+         * If the given predicate is an instance of {@code Filter<R>}, then the returned predicate
+         * is an instance of {@code Optimization.OnFilter<R>}.
          *
          * @param  other  the other predicate.
          * @return the {@code AND} logical operation between this filter and the given predicate.
@@ -253,8 +253,8 @@ public class Optimization {
 
         /**
          * Returns the {@code OR} logical operation between this filter and the given predicate.
-         * If the given predicate is an instance of {@link Filter}, then the returned predicate
-         * is an instance of {@code Optimization.OnFilter}.
+         * If the given predicate is an instance of {@code Filter<R>}, then the returned predicate
+         * is an instance of {@code Optimization.OnFilter<R>}.
          *
          * @param  other  the other predicate.
          * @return the {@code OR} logical operation between this filter and the given predicate.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java b/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
index 20d281e95e..cae63556d3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/PropertyValue.java
@@ -46,7 +46,7 @@ import org.opengis.filter.ValueReference;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  *
  * @param  <V>  the type of value computed by the expression.
  *
@@ -76,7 +76,7 @@ abstract class PropertyValue<V> extends LeafExpression<Feature,V>
     protected final boolean isVirtual;
 
     /**
-     * The prefix in a x-path for considering a property as virual.
+     * The prefix in a x-path for considering a property as virtual.
      */
     static final String VIRTUAL_PREFIX = "/*/";
 
@@ -133,6 +133,14 @@ split:  if (path != null) {
         return (path == null || path.isEmpty()) ? tip : new AssociationValue<>(path, tip);
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public final Class<Feature> getResourceClass() {
+        return Feature.class;
+    }
+
     /**
      * For {@link #toString()}, {@link #hashCode()} and {@link #equals(Object)} implementations.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/UnaryFunction.java b/core/sis-feature/src/main/java/org/apache/sis/filter/UnaryFunction.java
index 4cc5327b6c..cff832db14 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/UnaryFunction.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/UnaryFunction.java
@@ -65,6 +65,16 @@ class UnaryFunction<R,V> extends Node {
         this.expression = expression;
     }
 
+    /**
+     * Returns the class of resources expected by this filter.
+     * Defined for {@link Filter#getResourceClass()} implementations.
+     *
+     * @return type of resources accepted by this filter, or {@code null} if inconsistent.
+     */
+    public final Class<? super R> getResourceClass() {
+        return expression.getResourceClass();
+    }
+
     /**
      * Returns the expression used as parameter by this function.
      * Defined for {@link Expression#getParameters()} implementations.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ObservableImage.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ObservableImage.java
index 04942d510a..07279f0ef8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ObservableImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ObservableImage.java
@@ -36,7 +36,7 @@ import org.apache.sis.util.ArraysExt;
  * <p>This class should be used in preference to {@link BufferedImage} when the image may be the
  * source of {@link org.apache.sis.image.ImageProcessor} operations. It is the case In particular
  * when this image is given to {@link org.apache.sis.coverage.grid.GridCoverage2D} constructor.
- * We can not prevent {@link BufferedImage} to implement {@link WritableRenderedImage}, but we
+ * We cannot prevent {@link BufferedImage} to implement {@link WritableRenderedImage}, but we
  * can give a change to Apache SIS to be notified about modifications to pixel data.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
index edb9f26946..a9bcf55dc4 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/GeometryConverter.java
@@ -106,6 +106,14 @@ final class GeometryConverter<R,G> extends Node implements Optimization.OnExpres
         return NAME;
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return expression.getResourceClass();
+    }
+
     /**
      * Returns the expression used as parameters for this function.
      * This is the value specified at construction time.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/Node.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/Node.java
index f48299a752..581588efb3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/Node.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/Node.java
@@ -19,10 +19,10 @@ package org.apache.sis.internal.filter;
 import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.Collection;
-import java.io.Serializable;
 import java.util.Collections;
 import java.util.function.Predicate;
 import java.util.logging.Logger;
+import java.io.Serializable;
 import org.opengis.util.CodeList;
 import org.opengis.util.LocalName;
 import org.opengis.util.ScopedName;
@@ -96,6 +96,26 @@ public abstract class Node implements Serializable {
                                           type, 1, 1, null, (AttributeType<?>[]) null);
     }
 
+    /**
+     * Returns the most specialized class of the given pair of class. A specialized class is guaranteed to exist
+     * if parametrized type safety has not been bypassed with unchecked casts, because {@code <R>} is always valid.
+     * However this method is not guaranteed to be able to find that specialized type, because it could be none of
+     * the given arguments if {@code t1}, {@code t2} and {@code <R>} are interfaces with {@code <R>} extending both
+     * {@code t1} and {@code t2}.
+     *
+     * @param  <R>  the compile-time type of resources expected by filters or expressions.
+     * @param  t1   the runtime type of resources expected by the first filter or expression. May be null.
+     * @param  t2   the runtime type of resources expected by the second filter or expression. May be null.
+     * @return the most specialized type of resources, or {@code null} if it cannot be determined.
+     */
+    protected static <R> Class<? super R> specializedClass(final Class<? super R> t1, final Class<? super R> t2) {
+        if (t1 != null && t2 != null) {
+            if (t1.isAssignableFrom(t2)) return t2;
+            if (t2.isAssignableFrom(t1)) return t1;
+        }
+        return null;
+    }
+
     /**
      * Returns the mathematical symbol for this binary function.
      * For comparison operators, the symbol should be one of {@literal < > ≤ ≥ = ≠}.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionWithSRID.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionWithSRID.java
index daa6f702d8..b2640be641 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionWithSRID.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionWithSRID.java
@@ -206,6 +206,15 @@ search: if (crs instanceof CoordinateReferenceSystem) {
         }
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     * Subclasses should override this method.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return (srid != null) ? srid.getResourceClass() : Object.class;
+    }
+
     /**
      * Provides the type of values produced by this expression when a feature of the given type is evaluated.
      *
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/GeometryConstructor.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/GeometryConstructor.java
index a687d91527..bebef3918e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/GeometryConstructor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/GeometryConstructor.java
@@ -77,6 +77,14 @@ class GeometryConstructor<R,G> extends FunctionWithSRID<R> {
         return new GeometryConstructor<>(operation, effective, getGeometryLibrary());
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return specializedClass(geometry.getResourceClass(), super.getResourceClass());
+    }
+
     /**
      * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/OneGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/OneGeometry.java
index b1c7432548..2ce129b57d 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/OneGeometry.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/OneGeometry.java
@@ -26,7 +26,7 @@ import org.opengis.filter.Expression;
 
 /**
  * SQLMM spatial functions taking a single geometry operand.
- * This base class assume that the geometry is the only parameter.
+ * This base class assumes that the geometry is the only parameter.
  * Subclasses may add other kind of parameters.
  *
  * @author  Johann Sorel (Geomatys)
@@ -75,6 +75,14 @@ class OneGeometry<R,G> extends SpatialFunction<R> {
         return getGeometryLibrary(geometry);
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return geometry.getResourceClass();
+    }
+
     /**
      * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
      */
@@ -127,6 +135,14 @@ class OneGeometry<R,G> extends SpatialFunction<R> {
             return new WithArgument<>(operation, effective, getGeometryLibrary());
         }
 
+        /**
+         * Returns the class of resources expected by this expression.
+         */
+        @Override
+        public Class<? super R> getResourceClass() {
+            return specializedClass(super.getResourceClass(), argument.getResourceClass());
+        }
+
         /**
          * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
          */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
index 19d0e6ca91..eeb2809a65 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
@@ -98,6 +98,18 @@ final class ST_Point<R,G> extends FunctionWithSRID<R> {
         return library;
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        Class<? super R> type = super.getResourceClass();
+        for (final Expression<R,?> p : parameters) {
+            type = specializedClass(type, p.getResourceClass());
+        }
+        return type;
+    }
+
     /**
      * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
index 463b1e2660..887ae43dc4 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
@@ -93,6 +93,14 @@ final class ST_Transform<R,G> extends FunctionWithSRID<R> {
         return new ST_Transform<>(effective, getGeometryLibrary());
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return specializedClass(geometry.getResourceClass(), super.getResourceClass());
+    }
+
     /**
      * Returns a handler for the library of geometric objects used by this expression.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/TwoGeometries.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/TwoGeometries.java
index e394a62f31..ad06f76fa8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/TwoGeometries.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/TwoGeometries.java
@@ -115,6 +115,14 @@ class TwoGeometries<R,G> extends SpatialFunction<R> {
         return getGeometryLibrary(geometry1);
     }
 
+    /**
+     * Returns the class of resources expected by this expression.
+     */
+    @Override
+    public Class<? super R> getResourceClass() {
+        return specializedClass(geometry1.getResourceClass(), geometry2.getResourceClass());
+    }
+
     /**
      * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
      */
@@ -172,6 +180,14 @@ class TwoGeometries<R,G> extends SpatialFunction<R> {
             return new WithArgument<>(operation, effective, getGeometryLibrary());
         }
 
+        /**
+         * Returns the class of resources expected by this expression.
+         */
+        @Override
+        public Class<? super R> getResourceClass() {
+            return specializedClass(super.getResourceClass(), argument.getResourceClass());
+        }
+
         /**
          * Returns the sub-expressions that will be evaluated to provide the parameters to the function.
          */
diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/IdentifierFilterTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/IdentifierFilterTest.java
index 10d75b2326..d005888119 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/IdentifierFilterTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/IdentifierFilterTest.java
@@ -35,7 +35,7 @@ import org.opengis.filter.FilterFactory;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 public final class IdentifierFilterTest extends TestCase {
@@ -81,6 +81,7 @@ public final class IdentifierFilterTest extends TestCase {
         final Feature f3 = ftb.clear().setName("Test 3").build().newInstance();
 
         final Filter<Feature> id = factory.resourceId("123");
+        assertEquals(Feature.class, id.getResourceClass());
         assertTrue (id.test(f1));
         assertTrue (id.test(f2));
         assertFalse(id.test(f3));
@@ -103,6 +104,7 @@ public final class IdentifierFilterTest extends TestCase {
                 factory.resourceId("abc"),
                 factory.resourceId("123"));
 
+        assertEquals(Feature.class, id.getResourceClass());
         assertTrue (id.test(f1));
         assertTrue (id.test(f2));
         assertFalse(id.test(f3));
diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/LeafExpressionTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/LeafExpressionTest.java
index 1444efaf01..c1b80108a7 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/LeafExpressionTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/LeafExpressionTest.java
@@ -33,7 +33,7 @@ import org.opengis.filter.ValueReference;
  * Tests {@link LeafExpression}.
  *
  * @author  Johann Sorel (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 public final class LeafExpressionTest extends TestCase {
@@ -93,6 +93,7 @@ public final class LeafExpressionTest extends TestCase {
         final Feature f = ftb.setName("Test").build().newInstance();
 
         ValueReference<Feature,?> ref = factory.property("some_property");
+        assertEquals(Feature.class, ref.getResourceClass());
         assertNull(ref.apply(f));
         assertNull(ref.apply(null));
 
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java
index dc25a40a7c..5faece4129 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java
@@ -64,6 +64,14 @@ final class FunctionMock implements Expression<Map<String,?>, Object> {
         return Names.createScopedName(null, null, name);
     }
 
+    /**
+     * Returns the type of resources accepted by this mock.
+     */
+    @Override
+    public Class<Map> getResourceClass() {
+        return Map.class;
+    }
+
     /**
      * Returns the function parameters.
      */
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionNamesTest.java b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionNamesTest.java
index f3ee5e5c43..3ec880cfcd 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionNamesTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionNamesTest.java
@@ -60,6 +60,7 @@ public final class FunctionNamesTest extends TestCase {
      */
     private static abstract class FilterBase implements ComparisonOperator<Object> {
         @Override public List<Expression<Object,?>> getExpressions() {return List.of();}
+        @Override public Class<Object> getResourceClass() {return Object.class;}
         @Override public boolean test(Object resource) {return false;}
     }
 
@@ -121,6 +122,7 @@ public final class FunctionNamesTest extends TestCase {
         final var expression = new ValueReference<Object,Object>() {
             @Override public String getXPath()      {return null;}
             @Override public Object apply(Object o) {return null;}
+            @Override public Class<Object> getResourceClass() {return Object.class;}
             @Override public <N> Expression<Object,N> toValueType(Class<N> target) {
                 throw new UnsupportedOperationException();
             }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java
index 90eb9a1f75..0e56410095 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java
@@ -56,6 +56,14 @@ final class ValueReferenceMock<V> implements ValueReference<Map<String,?>, V> {
         this.type  = type;
     }
 
+    /**
+     * Returns the type of resources accepted by this mock.
+     */
+    @Override
+    public Class<Map> getResourceClass() {
+        return Map.class;
+    }
+
     /**
      * Returns the name of the property for which to get values.
      */


[sis] 02/02: Remove the `FilterNode` class, replaced by default implementation of `Optimization.OnFilter` methods.

Posted by de...@apache.org.
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

commit cb0796b4951fae9b9847cff17c8df15632aa86cf
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon May 1 15:10:04 2023 +0200

    Remove the `FilterNode` class, replaced by default implementation of `Optimization.OnFilter` methods.
---
 .../apache/sis/filter/BinaryGeometryFilter.java    |   3 +-
 .../org/apache/sis/filter/ComparisonFilter.java    |  13 ++-
 .../java/org/apache/sis/filter/FilterNode.java     | 114 ---------------------
 .../org/apache/sis/filter/IdentifierFilter.java    |  13 ++-
 .../java/org/apache/sis/filter/LikeFilter.java     |   3 +-
 .../java/org/apache/sis/filter/LogicalFilter.java  |   5 +-
 .../java/org/apache/sis/filter/Optimization.java   |  29 +++++-
 .../org/apache/sis/filter/LogicalFilterTest.java   |  21 +++-
 8 files changed, 75 insertions(+), 126 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
index 8b62b37a7f..5bb4d09ba6 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/BinaryGeometryFilter.java
@@ -26,6 +26,7 @@ import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.internal.feature.GeometryWrapper;
 import org.apache.sis.internal.feature.SpatialOperationContext;
 import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.internal.filter.Node;
 import org.apache.sis.util.ArgumentChecks;
 
 // Branch-dependent imports
@@ -55,7 +56,7 @@ import org.opengis.feature.PropertyNotFoundException;
  *
  * @since 1.1
  */
-abstract class BinaryGeometryFilter<R,G> extends FilterNode<R> implements SpatialOperator<R>, Optimization.OnFilter<R> {
+abstract class BinaryGeometryFilter<R,G> extends Node implements SpatialOperator<R>, Optimization.OnFilter<R> {
     /**
      * For cross-version compatibility.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
index 791e545b61..b593f590c3 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ComparisonFilter.java
@@ -36,6 +36,7 @@ import java.time.temporal.ChronoField;
 import java.time.temporal.Temporal;
 import org.apache.sis.math.Fraction;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.filter.Node;
 
 // Branch-dependent imports
 import org.opengis.filter.Filter;
@@ -796,7 +797,7 @@ abstract class ComparisonFilter<R> extends BinaryFunction<R,Object,Object>
      *
      * @see org.apache.sis.filter.LogicalFilter.And
      */
-    static final class Between<R> extends FilterNode<R> implements BetweenComparisonOperator<R> {
+    static final class Between<R> extends Node implements BetweenComparisonOperator<R>, Optimization.OnFilter<R> {
         /** For cross-version compatibility during (de)serialization. */
         private static final long serialVersionUID = -2434954008425799595L;
 
@@ -812,6 +813,14 @@ abstract class ComparisonFilter<R> extends BinaryFunction<R,Object,Object>
             this.upper = new    LessThanOrEqualTo<>(expression, upper, true, MatchAction.ANY);
         }
 
+        /**
+         * Creates a new filter of the same type but different parameters.
+         */
+        @Override
+        public Filter<R> recreate(final Expression<R,?>[] effective) {
+            return new Between<>(effective[0], effective[1], effective[2]);
+        }
+
         /** Returns the class of resources expected by this filter. */
         @Override public final Class<? super R> getResourceClass() {
             return specializedClass(lower.getResourceClass(),
@@ -828,7 +837,7 @@ abstract class ComparisonFilter<R> extends BinaryFunction<R,Object,Object>
 
         /** Returns the expression to be compared by this operator, together with boundaries. */
         @Override public List<Expression<R,?>> getExpressions() {
-            return List.of(lower.expression1, lower.expression2, upper.expression2);
+            return List.of(getExpression(), getLowerBoundary(), getUpperBoundary());
         }
 
         /** Returns the expression to be compared. */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java b/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java
deleted file mode 100644
index d35ad68a46..0000000000
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/FilterNode.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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;
-
-import java.util.function.Predicate;
-import org.apache.sis.internal.filter.Node;
-
-// Branch-dependent imports
-import org.opengis.filter.Filter;
-
-
-/**
- * Base class of some (not all) nodes that are filters. This base class overrides {@link Predicate}
- * methods for building other {@link Filter} objects instead of default Java implementations that
- * Apache SIS cannot recognize.
- *
- * <p><b>Note:</b> this class duplicates the method definition in {@link Optimization.OnFilter}.
- * This duplication exists because not all filter implementations extends this class, and not all
- * implementations implement the {@link Optimization.OnFilter} interface.</p>
- *
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
- *
- * @param  <R>  the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs.
- *
- * @since 1.1
- */
-abstract class FilterNode<R> extends Node implements Filter<R> {
-    /**
-     * For cross-version compatibility.
-     */
-    private static final long serialVersionUID = -1272149643938168189L;
-
-    /**
-     * Creates a new node.
-     */
-    FilterNode() {
-    }
-
-    /**
-     * If the given predicate can be casted to a filter of the same parameterized type as the template,
-     * returns {@code other} casted to that type. Otherwise returns {@code null}.
-     *
-     * @param  <R>       desired parameterized type.
-     * @param  template  the filter from which to get the runtime value of {@code <R>}.
-     * @param  other     the predicate to cast to a filter compatible with the target.
-     * @return the casted predicate, or {@code null} if it cannot be casted.
-     */
-    @SuppressWarnings("unchecked")
-    static <R> Filter<R> castOrNull(final Filter<R> template, final Predicate<? super R> other) {
-        if (other instanceof Filter<?>) {
-            final Class<?> type = template.getResourceClass();
-            if (type != null) {
-                final Class<?> to = ((Filter<?>) other).getResourceClass();
-                if (to != null && type.isAssignableFrom(to)) {
-                    return (Filter<R>) other;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the {@code AND} logical operation between this filter and the given predicate.
-     * This method duplicates the {@link Optimization.OnFilter#and(Predicate)} method, but is
-     * defined because not all subclasses implement the {@code Optimization} inner interface.
-     */
-    @Override
-    public final Predicate<R> and(final Predicate<? super R> other) {
-        final Filter<R> filter = castOrNull(this, other);
-        if (filter != null) {
-            return new LogicalFilter.And<>(this, filter);
-        } else {
-            return Filter.super.and(other);
-        }
-    }
-
-    /**
-     * Returns the {@code OR} logical operation between this filter and the given predicate.
-     * This method duplicates the {@link Optimization.OnFilter#or(Predicate)} method, but is
-     * defined because not all subclasses implement the {@code Optimization} inner interface.
-     */
-    @Override
-    public final Predicate<R> or(final Predicate<? super R> other) {
-        final Filter<R> filter = castOrNull(this, other);
-        if (filter != null) {
-            return new LogicalFilter.Or<>(this, filter);
-        } else {
-            return Filter.super.and(other);
-        }
-    }
-
-    /**
-     * Returns the logical negation of this filter.
-     */
-    @Override
-    public final Predicate<R> negate() {
-        return new LogicalFilter.Not<>(this);
-    }
-}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
index 3e2ec855a0..102fc4363e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/IdentifierFilter.java
@@ -19,12 +19,14 @@ package org.apache.sis.filter;
 import java.util.List;
 import java.util.Collection;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.filter.Node;
 import org.apache.sis.internal.feature.AttributeConvention;
 
 // Branch-dependent imports
 import org.opengis.feature.Feature;
 import org.opengis.filter.Expression;
 import org.opengis.filter.ResourceId;
+import org.opengis.filter.Filter;
 
 
 /**
@@ -36,7 +38,7 @@ import org.opengis.filter.ResourceId;
  * @version 1.4
  * @since   1.1
  */
-final class IdentifierFilter extends FilterNode<Feature> implements ResourceId<Feature> {
+final class IdentifierFilter extends Node implements ResourceId<Feature>, Optimization.OnFilter<Feature> {
     /**
      * For cross-version compatibility.
      */
@@ -55,6 +57,15 @@ final class IdentifierFilter extends FilterNode<Feature> implements ResourceId<F
         this.identifier = identifier;
     }
 
+    /**
+     * Nothing to optimize here. The {@code Optimization.OnFilter} interface
+     * is implemented for inheriting the AND, OR and NOT methods overriding.
+     */
+    @Override
+    public Filter<Feature> optimize(Optimization optimization) {
+        return this;
+    }
+
     /**
      * Returns the class of resources expected by this expression.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
index d2651378be..bc3a4ab79e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/LikeFilter.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Collection;
 import java.util.regex.Pattern;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.filter.Node;
 
 // Branch-dependent imports
 import org.opengis.filter.Filter;
@@ -38,7 +39,7 @@ import org.opengis.filter.LikeOperator;
  *
  * @since 1.1
  */
-final class LikeFilter<R> extends FilterNode<R> implements LikeOperator<R>, Optimization.OnFilter<R> {
+final class LikeFilter<R> extends Node implements LikeOperator<R>, Optimization.OnFilter<R> {
     /**
      * For cross-version compatibility.
      */
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java b/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
index 0bdde0b4e6..8dc2b23b28 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/LogicalFilter.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.Collection;
 import java.util.LinkedHashSet;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.internal.filter.Node;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 
@@ -41,7 +42,7 @@ import org.opengis.filter.LogicalOperatorName;
  *
  * @since 1.1
  */
-abstract class LogicalFilter<R> extends FilterNode<R> implements LogicalOperator<R>, Optimization.OnFilter<R> {
+abstract class LogicalFilter<R> extends Node implements LogicalOperator<R>, Optimization.OnFilter<R> {
     /**
      * For cross-version compatibility.
      */
@@ -223,7 +224,7 @@ abstract class LogicalFilter<R> extends FilterNode<R> implements LogicalOperator
      *
      * @param  <R>  the type of resources used as inputs.
      */
-    static final class Not<R> extends FilterNode<R> implements LogicalOperator<R>, Optimization.OnFilter<R> {
+    static final class Not<R> extends Node implements LogicalOperator<R>, Optimization.OnFilter<R> {
         /** For cross-version compatibility. */
         private static final long serialVersionUID = -1296823195138427781L;
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java b/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
index 2f1ca09039..0e6d2f985c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/Optimization.java
@@ -231,10 +231,31 @@ public class Optimization {
             return this;
         }
 
+        /**
+         * If the given predicate can be casted to a filter of the same parameterized type as this,
+         * returns {@code other} casted to that type. Otherwise returns {@code null}.
+         *
+         * @param  other  the predicate to cast to a filter compatible with this.
+         * @return the casted predicate, or {@code null} if it cannot be casted.
+         */
+        @SuppressWarnings("unchecked")
+        private Filter<R> castOrNull(final Predicate<? super R> other) {
+            if (other instanceof Filter<?>) {
+                final Class<?> type = getResourceClass();
+                if (type != null) {
+                    final Class<?> to = ((Filter<?>) other).getResourceClass();
+                    if (to != null && type.isAssignableFrom(to)) {
+                        return (Filter<R>) other;
+                    }
+                }
+            }
+            return null;
+        }
+
         /**
          * Returns the {@code AND} logical operation between this filter and the given predicate.
          * If the given predicate is an instance of {@code Filter<R>}, then the returned predicate
-         * is an instance of {@code Optimization.OnFilter<R>}.
+         * is also an instance of {@code Filter<R>}.
          *
          * @param  other  the other predicate.
          * @return the {@code AND} logical operation between this filter and the given predicate.
@@ -243,7 +264,7 @@ public class Optimization {
          */
         @Override
         default Predicate<R> and(final Predicate<? super R> other) {
-            final Filter<R> filter = FilterNode.castOrNull(this, other);
+            final Filter<R> filter = castOrNull(other);
             if (filter != null) {
                 return new LogicalFilter.And<>(this, filter);
             } else {
@@ -254,7 +275,7 @@ public class Optimization {
         /**
          * Returns the {@code OR} logical operation between this filter and the given predicate.
          * If the given predicate is an instance of {@code Filter<R>}, then the returned predicate
-         * is an instance of {@code Optimization.OnFilter<R>}.
+         * is also an instance of {@code Filter<R>}.
          *
          * @param  other  the other predicate.
          * @return the {@code OR} logical operation between this filter and the given predicate.
@@ -263,7 +284,7 @@ public class Optimization {
          */
         @Override
         default Predicate<R> or(final Predicate<? super R> other) {
-            final Filter<R> filter = FilterNode.castOrNull(this, other);
+            final Filter<R> filter = castOrNull(other);
             if (filter != null) {
                 return new LogicalFilter.Or<>(this, filter);
             } else {
diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/LogicalFilterTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/LogicalFilterTest.java
index c48f45871c..5111178236 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/LogicalFilterTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/LogicalFilterTest.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.Collection;
 import java.util.function.Function;
 import java.util.function.BiFunction;
+import java.util.function.Predicate;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
@@ -141,11 +142,19 @@ public final class LogicalFilterTest extends TestCase {
         assertArrayEquals(new Filter<?>[] {f1, f2}, filter.getOperands().toArray());
         assertEquals(expected, filter.test(null));
         assertSerializedEquals(filter);
-
+        /*
+         * Same test, using the constructor accepting any number of operands.
+         */
         filter = anyArity.apply(List.of(f1, f2, f1));
         assertArrayEquals(new Filter<?>[] {f1, f2, f1}, filter.getOperands().toArray());
         assertEquals(expected, filter.test(null));
         assertSerializedEquals(filter);
+        /*
+         * Test the `Predicate` methods, which should be overridden by `Optimization.OnFilter`.
+         */
+        assertInstanceOf("Predicate.and(…)",   Optimization.OnFilter.class, f1.and(f2));
+        assertInstanceOf("Predicate.or(…)",    Optimization.OnFilter.class, f1.or(f2));
+        assertInstanceOf("Predicate.negate()", Optimization.OnFilter.class, f1.negate());
     }
 
     /**
@@ -174,6 +183,16 @@ public final class LogicalFilterTest extends TestCase {
 
         assertFalse(factory.not(filterTrue ).test(feature));
         assertTrue (factory.not(filterFalse).test(feature));
+        /*
+         * Test the `Predicate` methods, which should be overridden by `Optimization.OnFilter`.
+         */
+        Predicate<Feature> predicate = filterTrue.and(filterFalse);
+        assertInstanceOf("Predicate.and(…)", Optimization.OnFilter.class, predicate);
+        assertFalse(predicate.test(feature));
+
+        predicate = filterTrue.or(filterFalse);
+        assertInstanceOf("Predicate.or(…)", Optimization.OnFilter.class, predicate);
+        assertTrue(predicate.test(feature));
     }
 
     /**