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 <martin.desruisse...@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)); } /**