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:06 UTC

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

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));
     }
 
     /**