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 c6a2901fddda0682003e457abbe6021691c64747 Merge: da1cc1c2df bd9026738e Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Mar 2 16:24:43 2023 +0100 Merge remote-tracking branch 'origin/feat/filtercopy' into geoapi-4.0. Modifications to the pull request: - Add the missing parameterized types. - Register action for each filter type using the mechanism provided by `Visitor` parent class. - Reuse previously existing filter or expression instances when possible. .../apache/sis/internal/filter/CopyVisitor.java | 712 +++++++++++++++++++++ .../apache/sis/internal/filter/EditVisitor.java | 56 ++ .../apache/sis/internal/filter/package-info.java | 2 +- .../sis/internal/filter/CopyVisitorTest.java | 77 +++ .../sis/internal/filter/FilterFactoryMock.java | 562 ++++++++++++++++ .../apache/sis/internal/filter/FunctionMock.java | 91 +++ .../sis/internal/filter/ValueReferenceMock.java | 82 +++ .../apache/sis/test/suite/FeatureTestSuite.java | 3 +- 8 files changed, 1583 insertions(+), 2 deletions(-) diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/filter/CopyVisitor.java index 0000000000,a3847de3c6..a02cf00328 mode 000000,100644..100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/CopyVisitor.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/CopyVisitor.java @@@ -1,0 -1,232 +1,712 @@@ + /* + * 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.internal.filter; + -import java.util.ArrayList; + import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import org.apache.sis.util.ArgumentChecks; -import org.opengis.filter.BetweenComparisonOperator; -import org.opengis.filter.BinaryComparisonOperator; -import org.opengis.filter.DistanceOperator; -import org.opengis.filter.Expression; -import org.opengis.filter.Filter; -import org.opengis.filter.FilterFactory; -import org.opengis.filter.LikeOperator; -import org.opengis.filter.Literal; -import org.opengis.filter.LogicalOperator; -import org.opengis.filter.MatchAction; -import org.opengis.filter.NilOperator; -import org.opengis.filter.NullOperator; -import org.opengis.filter.ValueReference; -import org.opengis.geometry.Envelope; ++import java.util.ArrayList; ++import java.util.Collection; ++import javax.measure.Quantity; ++import javax.measure.quantity.Length; + import org.opengis.util.CodeList; ++import org.opengis.geometry.Envelope; ++import org.apache.sis.util.ArgumentChecks; ++import org.apache.sis.util.resources.Errors; ++import org.apache.sis.internal.feature.Resources; ++import org.apache.sis.internal.util.CollectionsExt; ++ ++// Branch-dependent imports ++import org.opengis.filter.*; ++ + + /** - * Visitor used to copy expressions and filters from one factory to another. - * This class purpose is to offer a way to convert filters to different and - * often more specialized and efficient implementations such as for Coverages or SQL. ++ * Visitor used to copy expressions and filters with potentially a change of parameterized types. ++ * This class can be used when filters need to be recreated using a different {@link FilterFactory}, ++ * for example because the type of resources changed. For example different filter implementations ++ * may be needed if the filters need to operate on {@link org.apache.sis.coverage.grid.GridCoverage} ++ * resources instead of {@link org.opengis.feature.Feature} instances. + * - * @author Johann Sorel (Geomatys) ++ * @author Johann Sorel (Geomatys) ++ * @author Martin Desruisseaux (Geomatys) ++ * @version 1.4 ++ * ++ * @param <SR> the type of resources expected by the filters to copy (source resource). ++ * @param <TR> the type of resources expected by the copied filters (target resource). ++ * @param <G> base class of geometry objects. ++ * @param <T> base class of temporal objects. ++ * ++ * @since 1.4 + */ -public final class CopyVisitor<R,T> extends Visitor<R,AtomicReference> { ++public class CopyVisitor<SR,TR,G,T> extends Visitor<SR, List<Object>> { ++ /** ++ * The factory to use for creating the new filters and expressions. ++ * Note that some methods in this factory may return {@code null}. ++ * See <i>Partially implemented factory</i> in {@link EditVisitor} Javadoc. ++ */ ++ protected final FilterFactory<TR,G,T> factory; + - private final FilterFactory<T,Object,Object> targetFactory; ++ /** ++ * Whether to force creation of new filters or expressions even when the operands did not changed. ++ * If {@code false}, new filters and expressions are created only when at least one operand changed. ++ * This flag should be {@code true} if the {@code <SR>} and {@code <TR>} types are not the same, ++ * or if the user knows that the {@linkplain #factory} may create a different kind of object even ++ * when the operands are the same. ++ */ ++ private final boolean forceNew; + + /** - * Create a new copy visitor with given factory. ++ * Whether to force the use of newly created filters or expressions even when they are equal to the original ones. ++ * If {@code false}, then the previously existing filters or expressions will be reused when the newly created ++ * instances are equal according to {@link Object#equals(Object)}. Note this sharing requires that the filter ++ * or expression to reuse expects a base resource type {@code <R>} which is common to both {@code <? super SR>} ++ * and {@code <? super TR>}. We have no way to verify that. + * - * @param targetFactory not null ++ * <p>This flag should be {@code true} if the filters or expressions are mutable. ++ * Otherwise it may be {@code false} as a way to share existing instances.</p> + */ - public CopyVisitor(FilterFactory<T,?,?> targetFactory) { - ArgumentChecks.ensureNonNull("factory", targetFactory); - this.targetFactory = (FilterFactory<T, Object, Object>) targetFactory; - setBinaryComparisonHandlers(this::copyBinaryComparison); - setBinaryTemporalHandlers(this::copyBinaryTemporal); - setLogicalHandlers(this::copyLogical); - setSpatialHandlers(this::copySpatial); - setMathHandlers(this::copyMath); - } ++ private final boolean forceUse; + + /** - * Copy given filter using given factory. ++ * The factory method to invoke for creating a binary comparison operator. ++ * This is used for invoking one of the {@link FilterFactory} methods for ++ * a given {@link ComparisonOperatorName}. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(ComparisonOperatorName.PROPERTY_IS_EQUAL_TO, FilterFactory::equal); ++ * } + * - * @param filter filter to copy - * @return copied filter. ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * ++ * @see CopyVisitor#setCopyHandler(ComparisonOperatorName, BinaryComparisonFactory) + */ - public Filter<T> copy(Filter<R> filter) { - final AtomicReference ref = new AtomicReference(); - visit(filter, ref); - return (Filter) ref.get(); ++ @FunctionalInterface ++ protected interface BinaryComparisonFactory<R> { ++ /** ++ * Creates a new binary comparison operator from the given operands. ++ * ++ * @param factory the factory to use for creating the filter ++ * @param expression1 the first of the two expressions to be used by this comparator. ++ * @param expression2 the second of the two expressions to be used by this comparator. ++ * @param isMatchingCase specifies whether comparisons are case sensitive. ++ * @param matchAction specifies how the comparisons shall be evaluated for a collection of values. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ */ ++ BinaryComparisonOperator<R> create( ++ FilterFactory<R,?,?> factory, ++ Expression<? super R, ?> expression1, ++ Expression<? super R, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction); + } + + /** - * Copy given expression using given factory. ++ * The factory method to invoke for creating a temporal operator. ++ * This is used for invoking one of the {@link FilterFactory} methods ++ * for a given {@link TemporalOperatorName}. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(TemporalOperatorName.AFTER, FilterFactory::after); ++ * } + * - * @param expression expression to copy - * @return copied expression. ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * @param <T> base class of temporal objects specified by the enclosing class. ++ * ++ * @see CopyVisitor#setCopyHandler(TemporalOperatorName, TemporalComparisonFactory) + */ - public Expression<T,Object> copy(Expression expression) { - final AtomicReference<Expression<T, Object>> ref = new AtomicReference(); - visit(expression, ref); - return ref.get(); ++ @FunctionalInterface ++ protected interface TemporalFactory<R,T> { ++ /** ++ * Creates a new temporal operator from the given operands. ++ * ++ * @param factory the factory to use for creating the filter. ++ * @param time1 expression fetching the first temporal value. ++ * @param time2 expression fetching the second temporal value. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ */ ++ TemporalOperator<R> create( ++ FilterFactory<R,?,T> factory, ++ Expression<? super R, ? extends T> time1, ++ Expression<? super R, ? extends T> time2); + } + - private List<Expression<T,Object>> copyExpressions(List<Expression<? super R,?>> source) { - final List<Expression<T,Object>> results = new ArrayList<>(source.size()); - for (Expression e : source) { - results.add(copy(e)); ++ /** ++ * The factory method to invoke for creating a spatial operator. ++ * This is used for invoking one of the {@link FilterFactory} methods ++ * for a given {@link SpatialOperatorName}. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(SpatialOperatorName.EQUALS, FilterFactory::equals); ++ * } ++ * ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * @param <G> base class of geometry objects specified by the enclosing class. ++ * ++ * @see CopyVisitor#setCopyHandler(SpatialOperatorName, SpatialFactory) ++ */ ++ @FunctionalInterface ++ protected interface SpatialFactory<R,G> { ++ /** ++ * Creates a new spatial operator from the given operands. ++ * ++ * @param factory the factory to use for creating the filter. ++ * @param geometry1 expression fetching the first geometry of the binary operator. ++ * @param geometry2 expression fetching the second geometry of the binary operator. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ */ ++ BinarySpatialOperator<R> create( ++ FilterFactory<R,G,?> factory, ++ Expression<? super R, ? extends G> geometry1, ++ Expression<? super R, ? extends G> geometry2); ++ ++ /** ++ * Bridge for the "bbox" operation, because it uses a different method signature. ++ * Usage example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(SpatialOperatorName.BBOX, SpatialFactory::bbox); ++ * } ++ * ++ * @param <R> the {@link CopyVisitor} type of resources. ++ * @param <G> base class of geometry objects specified by {@code CopyVisitor}. ++ * @param factory the factory to use for creating the filter. ++ * @param geometry1 expression fetching the geometry to check for interaction with bounds. ++ * @param geometry2 the bounds to check geometry against. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ * ++ * @see SpatialOperatorName#BBOX ++ */ ++ static <R,G> BinarySpatialOperator<R> bbox( ++ final FilterFactory<R,G,?> factory, ++ final Expression<? super R, ? extends G> geometry1, ++ final Expression<? super R, ? extends G> geometry2) ++ { ++ if (geometry2 instanceof Literal<?,?>) { ++ final Object bounds = ((Literal<?,?>) geometry2).getValue(); ++ if (bounds instanceof Envelope) { ++ return factory.bbox(geometry1, (Envelope) bounds); ++ } ++ } ++ return null; + } - return results; + } + - private List<Filter<T>> copyOperands(List<Filter<R>> source) { - final List<Filter<T>> results = new ArrayList<>(source.size()); - for (Filter<R> e : source) { - results.add(copy(e)); ++ /** ++ * The factory method to invoke for creating a distance operator. ++ * This is used for invoking one of the {@link FilterFactory} methods ++ * for a given {@link DistanceOperatorName}. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(DistanceOperatorName.BEYOND, FilterFactory::beyond); ++ * } ++ * ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * @param <G> base class of geometry objects specified by the enclosing class. ++ * ++ * @see CopyVisitor#setCopyHandler(DistanceOperatorName, DistanceFactory) ++ */ ++ @FunctionalInterface ++ protected interface DistanceFactory<R,G> { ++ /** ++ * Creates a new spatial operator from the given operands. ++ * ++ * @param factory the factory to use for creating the filter. ++ * @param geometry1 expression fetching the first geometry of the binary operator. ++ * @param geometry2 expression fetching the second geometry of the binary operator. ++ * @param distance distance for evaluating the expression as {@code true}. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ */ ++ DistanceOperator<R> create( ++ FilterFactory<R,G,?> factory, ++ Expression<? super R, ? extends G> geometry1, ++ Expression<? super R, ? extends G> geometry2, ++ Quantity<Length> distance); ++ } ++ ++ /** ++ * The factory method to invoke for creating a logical operator. ++ * This is used for invoking one of the {@link FilterFactory} ++ * methods for a given {@link LogicalOperatorName}. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(LogicalOperatorName.AND, FilterFactory::and); ++ * } ++ * ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * ++ * @see CopyVisitor#setCopyHandler(LogicalOperatorName, LogicalFactory) ++ */ ++ @FunctionalInterface ++ protected interface LogicalFactory<R> { ++ /** ++ * Creates a new binary comparison operator from the given operands. ++ * ++ * @param factory the factory to use for creating the filter. ++ * @param operands a collection of operands. ++ * @return the new filter, or {@code null} if this method cannot create a filter from the given arguments. ++ */ ++ LogicalOperator<R> create( ++ FilterFactory<R,?,?> factory, ++ Collection<? extends Filter<? super R>> operands); ++ ++ /** ++ * Bridge for the "not" operation, because it uses a different method signature. ++ * This method signature shall be identical to {@code create(…)} method signature. ++ * Usage example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler(LogicalOperatorName.NOT, LogicalFactory::not); ++ * } ++ * ++ * @param <R> the {@link CopyVisitor} type of resources. ++ * @param factory the factory to use for creating the filter. ++ * @param operands the operand of the NOT operation. ++ * @return a filter evaluating {@code NOT operand}. ++ */ ++ static <R> LogicalOperator<R> not( ++ final FilterFactory<R,?,?> factory, ++ final Collection<? extends Filter<? super R>> operands) ++ { ++ Filter<? super R> op = CollectionsExt.singletonOrNull(operands); ++ return (op != null) ? factory.not(op) : null; + } - return results; - } - - private void copyBinaryComparison(Filter<R> t, AtomicReference u) { - final BinaryComparisonOperator co = (BinaryComparisonOperator) t; - final List<Expression<T, Object>> exps = copyExpressions(t.getExpressions()); - final MatchAction ma = co.getMatchAction(); - final boolean mc = co.isMatchingCase(); - final Filter<T> result; - switch (t.getOperatorType().identifier()) { - case "PropertyIsEqualTo" : result = targetFactory.equal(exps.get(0), exps.get(1), mc, ma); break; - case "PropertyIsNotEqualTo" : result = targetFactory.notEqual(exps.get(0), exps.get(1), mc, ma); break; - case "PropertyIsLessThan" : result = targetFactory.less(exps.get(0), exps.get(1), mc, ma); break; - case "PropertyIsGreaterThan" : result = targetFactory.greater(exps.get(0), exps.get(1), mc, ma); break; - case "PropertyIsLessThanOrEqualTo" : result = targetFactory.lessOrEqual(exps.get(0), exps.get(1), mc, ma); break; - case "PropertyIsGreaterThanOrEqualTo" : result = targetFactory.greaterOrEqual(exps.get(0), exps.get(1), mc, ma); break; - default : throw new IllegalArgumentException("Unknowned filter type " + t.getOperatorType().identifier()); ++ } ++ ++ /** ++ * The factory method to invoke for creating a binary function. ++ * This is used for invoking one of the {@link FilterFactory} ++ * methods for a given arithmetic function name. Example: ++ * ++ * {@snippet lang="java" : ++ * copyVisitor.setCopyHandler("Multiply", FilterFactory::multiply); ++ * } ++ * ++ * @param <R> the same type of resources specified by the enclosing {@link CopyVisitor} class. ++ * ++ * @see CopyVisitor#setCopyHandler(String, BinaryFunctionFactory) ++ */ ++ @FunctionalInterface ++ protected interface BinaryFunctionFactory<R> { ++ /** ++ * Creates a new binary function from the given operands. ++ * ++ * @param factory the factory to use for creating the filter. ++ * @param operand1 the first of the two expressions to be used by this function. ++ * @param operand2 the second of the two expressions to be used by this function. ++ * @return the new expression, or {@code null} if this method cannot create an expression from the given arguments. ++ */ ++ Expression<R,Number> create( ++ FilterFactory<R,?,?> factory, ++ Expression<? super R, ? extends Number> operand1, ++ Expression<? super R, ? extends Number> operand2); ++ } ++ ++ /** ++ * Creates a new copy visitor with the given factory. ++ * ++ * @param factory the factory to use for creating the new filters and expressions. ++ * @param force whether to force new filters or expressions even when existing instances could be reused. ++ */ ++ public CopyVisitor(final FilterFactory<TR,G,T> factory, final boolean force) { ++ this(factory, true, force); ++ } ++ ++ /** ++ * Creates a new copy visitor with the given factory. ++ * The {@code forceNew} argument can be {@code false} ++ * only if {@code <SR>} and {@code <TR>} are the same type. ++ * ++ * @param newFactory the factory to use for creating the new filters and expressions. ++ * @param forceNew whether to force creation of new filters or expressions even when the operands did not changed. ++ * @param forceUse whether to force the use of newly created filters or expressions even when they are equal to the original ones. ++ */ ++ CopyVisitor(final FilterFactory<TR,G,T> newFactory, final boolean forceNew, final boolean forceUse) { ++ ArgumentChecks.ensureNonNull("factory", newFactory); ++ this.factory = newFactory; ++ this.forceNew = forceNew; ++ this.forceUse = forceUse; ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_EQUAL_TO, FilterFactory::equal); ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_NOT_EQUAL_TO, FilterFactory::notEqual); ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_LESS_THAN, FilterFactory::less); ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_GREATER_THAN, FilterFactory::greater); ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_LESS_THAN_OR_EQUAL_TO, FilterFactory::lessOrEqual); ++ setCopyHandler(ComparisonOperatorName.PROPERTY_IS_GREATER_THAN_OR_EQUAL_TO, FilterFactory::greaterOrEqual); ++ setCopyHandler( TemporalOperatorName.AFTER, FilterFactory::after); ++ setCopyHandler( TemporalOperatorName.BEFORE, FilterFactory::before); ++ setCopyHandler( TemporalOperatorName.BEGINS, FilterFactory::begins); ++ setCopyHandler( TemporalOperatorName.BEGUN_BY, FilterFactory::begunBy); ++ setCopyHandler( TemporalOperatorName.CONTAINS, FilterFactory::tcontains); ++ setCopyHandler( TemporalOperatorName.DURING, FilterFactory::during); ++ setCopyHandler( TemporalOperatorName.EQUALS, FilterFactory::tequals); ++ setCopyHandler( TemporalOperatorName.OVERLAPS, FilterFactory::toverlaps); ++ setCopyHandler( TemporalOperatorName.MEETS, FilterFactory::meets); ++ setCopyHandler( TemporalOperatorName.ENDS, FilterFactory::ends); ++ setCopyHandler( TemporalOperatorName.OVERLAPPED_BY, FilterFactory::overlappedBy); ++ setCopyHandler( TemporalOperatorName.MET_BY, FilterFactory::metBy); ++ setCopyHandler( TemporalOperatorName.ENDED_BY, FilterFactory::endedBy); ++ setCopyHandler( TemporalOperatorName.ANY_INTERACTS, FilterFactory::anyInteracts); ++ setCopyHandler( SpatialOperatorName.BBOX, SpatialFactory::bbox); ++ setCopyHandler( SpatialOperatorName.EQUALS, FilterFactory::equals); ++ setCopyHandler( SpatialOperatorName.DISJOINT, FilterFactory::disjoint); ++ setCopyHandler( SpatialOperatorName.INTERSECTS, FilterFactory::intersects); ++ setCopyHandler( SpatialOperatorName.TOUCHES, FilterFactory::touches); ++ setCopyHandler( SpatialOperatorName.CROSSES, FilterFactory::crosses); ++ setCopyHandler( SpatialOperatorName.WITHIN, FilterFactory::within); ++ setCopyHandler( SpatialOperatorName.CONTAINS, FilterFactory::contains); ++ setCopyHandler( SpatialOperatorName.OVERLAPS, FilterFactory::overlaps); ++ setCopyHandler( DistanceOperatorName.WITHIN, FilterFactory::within); ++ setCopyHandler( DistanceOperatorName.BEYOND, FilterFactory::beyond); ++ setCopyHandler( LogicalOperatorName.AND, FilterFactory::and); ++ setCopyHandler( LogicalOperatorName.OR, FilterFactory::or); ++ setCopyHandler( LogicalOperatorName.NOT, LogicalFactory::not); ++ setCopyHandler( FunctionNames.Add, FilterFactory::add); ++ setCopyHandler( FunctionNames.Subtract, FilterFactory::subtract); ++ setCopyHandler( FunctionNames.Multiply, FilterFactory::multiply); ++ setCopyHandler( FunctionNames.Divide, FilterFactory::divide); ++ /* ++ * Following are factory methods with different signatures, but where each signature appears only once. ++ * It is not worth to create e.g. a `TrinaryComparisonFactory` functional interface for only one method. ++ */ ++ setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_BETWEEN), (filter, accumulator) -> { ++ BetweenComparisonOperator<TR> target = null; ++ BetweenComparisonOperator<SR> source = (BetweenComparisonOperator<SR>) filter; ++ var exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 2) { ++ target = factory.between(exps.get(0), exps.get(1), exps.get(2)); ++ } ++ accept(accumulator, source, target); ++ }); ++ setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_LIKE), (filter, accumulator) -> { ++ LikeOperator<TR> target = null; ++ LikeOperator<SR> source = (LikeOperator<SR>) filter; ++ var exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 2) { ++ final Expression<?,?> p2 = exps.get(1); ++ if (p2 instanceof Literal<?,?>) { ++ final Object literal = ((Literal<?,?>) p2).getValue(); ++ if (literal instanceof String) { ++ target = factory.like(exps.get(0), (String) literal, source.getWildCard(), ++ source.getSingleChar(), source.getEscapeChar(), source.isMatchingCase()); ++ } ++ } ++ } ++ accept(accumulator, source, target); ++ }); ++ setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NIL), (filter, accumulator) -> { ++ NilOperator<TR> target = null; ++ NilOperator<SR> source = (NilOperator<SR>) filter; ++ var exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 1) { ++ target = factory.isNil(exps.get(0), source.getNilReason().orElse(null)); ++ } ++ accept(accumulator, source, target); ++ }); ++ setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NULL), (filter, accumulator) -> { ++ NullOperator<TR> target = null; ++ NullOperator<SR> source = (NullOperator<SR>) filter; ++ var exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 1) { ++ target = factory.isNull(exps.get(0)); ++ } ++ accept(accumulator, source, target); ++ }); ++ setExpressionHandler(FunctionNames.Literal, (expression, accumulator) -> { ++ Literal<SR,?> source = (Literal<SR,?>) expression; ++ Literal<TR,?> target = factory.literal(source.getValue()); ++ accept(accumulator, source, target); ++ }); ++ setExpressionHandler(FunctionNames.ValueReference, (expression, accumulator) -> { ++ ValueReference<SR,?> source = (ValueReference<SR,?>) expression; ++ ValueReference<TR,?> target = factory.property(source.getXPath()); ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given type of binary comparison operator. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler(ComparisonOperatorName.PROPERTY_IS_EQUAL_TO, FilterFactory::equal); ++ * } ++ * ++ * @param type identification of the filter type. ++ * @param action the action to execute when the identified filter is found. ++ */ ++ protected final void setCopyHandler(final ComparisonOperatorName type, final BinaryComparisonFactory<TR> action) { ++ setFilterHandler(type, (filter, accumulator) -> { ++ BinaryComparisonOperator<TR> target = null; ++ BinaryComparisonOperator<SR> source = (BinaryComparisonOperator<SR>) filter; ++ var exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 2) { ++ target = action.create(factory, exps.get(0), exps.get(1), source.isMatchingCase(), source.getMatchAction()); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given type of temporal operator. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler(TemporalOperatorName.AFTER, FilterFactory::after); ++ * } ++ * ++ * @param type identification of the filter type. ++ * @param action the action to execute when the identified filter is found. ++ */ ++ protected final void setCopyHandler(final TemporalOperatorName type, final TemporalFactory<TR,T> action) { ++ setFilterHandler(type, (filter, accumulator) -> { ++ TemporalOperator<TR> target = null; ++ TemporalOperator<SR> source = (TemporalOperator<SR>) filter; ++ List<Expression<TR,T>> exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 2) { ++ target = action.create(factory, exps.get(0), exps.get(1)); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given type of spatial operator. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler(SpatialOperatorName.EQUALS, FilterFactory::equals); ++ * } ++ * ++ * @param type identification of the filter type. ++ * @param action the action to execute when the identified filter is found. ++ */ ++ protected final void setCopyHandler(final SpatialOperatorName type, final SpatialFactory<TR,G> action) { ++ setFilterHandler(type, (filter, accumulator) -> { ++ BinarySpatialOperator<TR> target = null; ++ BinarySpatialOperator<SR> source = (BinarySpatialOperator<SR>) filter; ++ List<Expression<TR,G>> exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 2) { ++ target = action.create(factory, exps.get(0), exps.get(1)); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given type of distance operator. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler(DistanceOperatorName.BEYOND, FilterFactory::beyond); ++ * } ++ * ++ * @param type identification of the filter type. ++ * @param action the action to execute when the identified filter is found. ++ */ ++ protected final void setCopyHandler(final DistanceOperatorName type, final DistanceFactory<TR,G> action) { ++ setFilterHandler(type, (filter, accumulator) -> { ++ DistanceOperator<TR> target = null; ++ DistanceOperator<SR> source = (DistanceOperator<SR>) filter; ++ List<Expression<TR,G>> exps = copyExpressions(source.getExpressions()); ++ if (exps != null && exps.size() == 3) { ++ target = action.create(factory, exps.get(0), exps.get(1), source.getDistance()); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given type of logical operator. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler(LogicalOperatorName.AND, FilterFactory::and); ++ * } ++ * ++ * @param type identification of the filter type. ++ * @param action the action to execute when the identified filter is found. ++ */ ++ protected final void setCopyHandler(final LogicalOperatorName type, final LogicalFactory<TR> action) { ++ setFilterHandler(type, (filter, accumulator) -> { ++ LogicalOperator<TR> target = null; ++ LogicalOperator<SR> source = (LogicalOperator<SR>) filter; ++ final var exps = copyFilters(source.getOperands()); ++ if (exps != null) { ++ target = action.create(factory, exps); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Sets the action to execute for the given function. ++ * Example: ++ * ++ * {@snippet lang="java" : ++ * setCopyHandler("Multiply", FilterFactory::multiply); ++ * } ++ * ++ * @param name identification of the function. ++ * @param action the action to execute when the identified function is found. ++ */ ++ protected final void setCopyHandler(final String name, final BinaryFunctionFactory<TR> action) { ++ setExpressionHandler(name, (source, accumulator) -> { ++ Expression<TR,?> target = null; ++ List<Expression<TR,Number>> exps = copyExpressions(source.getParameters()); ++ if (exps != null) { ++ target = action.create(factory, exps.get(0), exps.get(1)); ++ } ++ accept(accumulator, source, target); ++ }); ++ } ++ ++ /** ++ * Adds the target filter or expression in the list of copied elements. ++ * ++ * @param accumulator the list of copied elements where to add the {@code target} element. ++ * @param source the original filter or expression which has been copied. ++ * @param target the copied filter or expression, or {@code null} if the copy could not be done. ++ * @throws IllegalArgumentException if {@code source} copy was mandated and couldn't be done. ++ */ ++ private void accept(final List<Object> accumulator, final Object source, Object target) { ++ if (target == null) { ++ if (forceNew) { ++ throw new IllegalArgumentException(Errors.format(Errors.Keys.CanNotCopy_1, source)); ++ } ++ target = source; ++ } else if (!forceUse && target.equals(source)) { ++ target = source; + } - u.set(result); - } - - private void copyBinaryTemporal(Filter<R> t, AtomicReference u) { - final List<Expression<T, Object>> exps = copyExpressions(t.getExpressions()); - final Filter<T> result; - switch (t.getOperatorType().identifier()) { - case "After" : result = targetFactory.after(exps.get(0), exps.get(1)); break; - case "Before" : result = targetFactory.before(exps.get(0), exps.get(1)); break; - case "Begins" : result = targetFactory.begins(exps.get(0), exps.get(1)); break; - case "BegunBy" : result = targetFactory.begunBy(exps.get(0), exps.get(1)); break; - case "TContains" : result = targetFactory.tcontains(exps.get(0), exps.get(1)); break; - case "During" : result = targetFactory.during(exps.get(0), exps.get(1)); break; - case "TEquals" : result = targetFactory.tequals(exps.get(0), exps.get(1)); break; - case "TOverlaps" : result = targetFactory.toverlaps(exps.get(0), exps.get(1)); break; - case "Meets" : result = targetFactory.meets(exps.get(0), exps.get(1)); break; - case "Ends" : result = targetFactory.ends(exps.get(0), exps.get(1)); break; - case "OverlappedBy" : result = targetFactory.overlappedBy(exps.get(0), exps.get(1)); break; - case "MetBy" : result = targetFactory.metBy(exps.get(0), exps.get(1)); break; - case "EndedBy" : result = targetFactory.endedBy(exps.get(0), exps.get(1)); break; - case "AnyInteracts" : result = targetFactory.anyInteracts(exps.get(0), exps.get(1)); break; - default : throw new IllegalArgumentException("Unknowned filter type " + t.getOperatorType().identifier()); ++ accumulator.add(target); ++ } ++ ++ /** ++ * Copies all expressions that are in the given list. ++ * The returned list has the same length than the given list. ++ * If all copied expressions are equal to the original expressions and {@link #forceNew} is {@code false}, ++ * then this method returns {@code null} for telling that filter or expression does not need to be created. ++ * ++ * <h4>Note on parameterized types</h4> ++ * This method cannot guarantee that the elements in the returned list have really the parameterized types ++ * declared in this method signature. They <em>should be</em> if the {@link FilterFactory}, {@link Filter} ++ * and {@link Expression} methods invoked by the caller fulfill the API contract. For example the operands ++ * of an arithmetic function should have {@link Number} value. But we have no way to verify that. ++ * ++ * @param <V> expected type of values returned by the expressions. This type <em>is not verified</em>, ++ * so this method is not really type-safe. This parameterized type is nevertheless used for more ++ * convenient casts by the callers, but should not be used outside private methods. ++ * @param source the list of expressions to copy. ++ * @return the copies expressions, or {@code null} if no copy is needed. ++ */ ++ @SuppressWarnings({"unchecked","rawtypes"}) ++ private <V> List<Expression<TR,V>> copyExpressions(final List<Expression<? super SR, ?>> source) { ++ final List<Object> results = new ArrayList<>(source.size()); ++ for (final Expression<? super SR, ?> e : source) { ++ visit((Expression<SR,?>) e, results); + } - u.set(result); - } - - private void copyLogical(Filter<R> t, AtomicReference u) { - final LogicalOperator co = (LogicalOperator) t; - final List<Filter<T>> ops = copyOperands(co.getOperands()); - final Filter<T> result; - switch (t.getOperatorType().identifier()) { - case "And" : result = targetFactory.and(ops); break; - case "Or" : result = targetFactory.or(ops); break; - case "Not" : result = targetFactory.not(ops.get(0)); break; - default : throw new IllegalArgumentException("Unknowned filter type " + t.getOperatorType().identifier()); ++ // Cast to <TR> is safe because of factory method signatures. ++ return !forceNew && results.equals(source) ? null : (List) results; ++ } ++ ++ /** ++ * Copies all filters that are in the given list. ++ * The returned list has the same length than the given list. ++ * If all copied filters are equal to the original filters and {@link #forceNew} is {@code false}, ++ * then this method returns {@code null} for telling that filter does not need to be created. ++ * ++ * @param source the list of filters to copy. ++ * @return the copies filters, or {@code null} if no copy is needed. ++ */ ++ @SuppressWarnings({"unchecked","rawtypes"}) ++ private List<Filter<TR>> copyFilters(final List<Filter<? super SR>> source) { ++ final var results = new ArrayList<Object>(source.size()); ++ for (final Filter<? super SR> e : source) { ++ visit((Filter<SR>) e, results); + } - u.set(result); - } - - private void copySpatial(Filter<R> t, AtomicReference u) { - final List<Expression<T, Object>> exps = copyExpressions(t.getExpressions()); - final Filter<T> result; - switch (t.getOperatorType().identifier()) { - case "BBOX" : result = targetFactory.bbox(exps.get(0), (Envelope) exps.get(1).apply(null)); break; - case "Equals" : result = targetFactory.equals(exps.get(0), exps.get(1)); break; - case "Disjoint" : result = targetFactory.disjoint(exps.get(0), exps.get(1)); break; - case "Intersects" : result = targetFactory.intersects(exps.get(0), exps.get(1)); break; - case "Touches" : result = targetFactory.touches(exps.get(0), exps.get(1)); break; - case "Crosses" : result = targetFactory.crosses(exps.get(0), exps.get(1)); break; - case "Within" : result = targetFactory.within(exps.get(0), exps.get(1)); break; - case "Contains" : result = targetFactory.contains(exps.get(0), exps.get(1)); break; - case "Overlaps" : result = targetFactory.overlaps(exps.get(0), exps.get(1)); break; - case "DWithin" : result = targetFactory.within(exps.get(0), exps.get(1), ((DistanceOperator) t).getDistance()); break; - case "Beyond" : result = targetFactory.beyond(exps.get(0), exps.get(1), ((DistanceOperator) t).getDistance()); break; - default : throw new IllegalArgumentException("Unknowned filter type " + t.getOperatorType().identifier()); ++ // Cast to <TR> is safe because of factory method signatures. ++ return !forceNew && results.equals(source) ? null : (List) results; ++ } ++ ++ /** ++ * Copies the given filter using the factory specified at construction time. ++ * ++ * @param source the filter to copy. ++ * @return the copied filter. ++ * @throws IllegalArgumentException if the filter cannot be copied. ++ */ ++ @SuppressWarnings("unchecked") ++ public Filter<TR> copy(final Filter<SR> source) { ++ final var accumulator = new ArrayList<Object>(1); ++ visit(source, accumulator); ++ switch (accumulator.size()) { ++ case 0: return null; ++ case 1: return (Filter<TR>) accumulator.get(0); ++ default: throw new AssertionError(accumulator); // Should never happen. + } - u.set(result); - } - - private void copyMath(Expression<R,?> t, AtomicReference u) { - final List<Expression> exps = (List) copyExpressions(t.getParameters()); - final Expression<T,?> result; - switch (t.getFunctionName().toString()) { - case FunctionNames.Add : result = targetFactory.add(exps.get(0), exps.get(1)); break; - case FunctionNames.Subtract : result = targetFactory.subtract(exps.get(0), exps.get(1)); break; - case FunctionNames.Multiply : result = targetFactory.multiply(exps.get(0), exps.get(1)); break; - case FunctionNames.Divide : result = targetFactory.divide(exps.get(0), exps.get(1)); break; - default : throw new IllegalArgumentException("Unknowned expression type " + t.getFunctionName().toString()); ++ } ++ ++ /** ++ * Copies the given expression using the factory specified at construction time. ++ * ++ * @param <V> the type of values computed by the expression. ++ * @param source the expression to copy. ++ * @return the copied expression. ++ * @throws IllegalArgumentException if the expression cannot be copied. ++ */ ++ @SuppressWarnings("unchecked") ++ public <V> Expression<TR,V> copy(final Expression<SR,V> source) { ++ final var accumulator = new ArrayList<Object>(1); ++ visit(source, accumulator); ++ switch (accumulator.size()) { ++ case 0: return null; ++ case 1: return (Expression<TR,V>) accumulator.get(0); ++ default: throw new AssertionError(accumulator); // Should never happen. + } - u.set(result); + } + ++ /** ++ * Invoked when no copy operation is registered for the given filter. ++ * The default implementation throws an {@link IllegalArgumentException}. ++ * ++ * @param type the filter type which has not been found. ++ * @param filter the filter to copy. ++ * @param accumulator where to add filters. ++ * @throws IllegalArgumentException if a copy of the given filter was required by cannot be performed. ++ */ + @Override - protected void typeNotFound(CodeList<?> type, Filter<R> filter, AtomicReference u) { - if (filter instanceof BetweenComparisonOperator) { - final BetweenComparisonOperator op = (BetweenComparisonOperator) filter; - u.set(targetFactory.between(copy(op.getExpression()), copy(op.getLowerBoundary()), copy(op.getUpperBoundary()))); - } else if (filter instanceof LikeOperator) { - final LikeOperator<R> op = (LikeOperator) filter; - u.set(targetFactory.like( - copy(op.getExpressions().get(0)), - (String) copy(op.getExpressions().get(1)).apply(null), - op.getWildCard(), - op.getSingleChar(), - op.getEscapeChar(), - op.isMatchingCase())); - } else if (filter instanceof NilOperator) { - final NilOperator<R> op = (NilOperator) filter; - u.set(targetFactory.isNil( - copy(op.getExpressions().get(0)), - op.getNilReason().orElse(null))); - } else if (filter instanceof NullOperator) { - final NullOperator<R> op = (NullOperator) filter; - u.set(targetFactory.isNull(copy(op.getExpressions().get(0)))); - } else { - super.typeNotFound(type, filter, u); ++ protected void typeNotFound(final CodeList<?> type, final Filter<SR> filter, final List<Object> accumulator) { ++ if (forceNew) { ++ throw new IllegalArgumentException(Resources.format(Resources.Keys.CanNotVisit_2, 0, type)); + } ++ accumulator.add(filter); + } + ++ /** ++ * Invoked when no copy operation is registered for the given expression. ++ * The default implementation creates a new function of the same name using the generic API. ++ * ++ * @param name the expression type which has not been found. ++ * @param expression the expression. ++ * @param accumulator where to add expressions. ++ * @throws IllegalArgumentException if an expression cannot be copied. ++ */ + @Override - protected void typeNotFound(String type, Expression<R, ?> expression, AtomicReference u) { - if (expression instanceof Literal) { - final Literal exp = (Literal) expression; - u.set(targetFactory.literal(exp.getValue())); - } else if (expression instanceof ValueReference) { - final ValueReference exp = (ValueReference) expression; - u.set(targetFactory.property(exp.getXPath())); - } else { - u.set(targetFactory.function(type, copyExpressions(expression.getParameters()).toArray(new Expression[0]))); ++ @SuppressWarnings("unchecked") ++ protected void typeNotFound(final String name, Expression<SR, ?> expression, final List<Object> accumulator) { ++ var exps = copyExpressions(expression.getParameters()); ++ if (exps != null) { ++ expression = factory.function(name, exps.toArray(Expression[]::new)); + } ++ accumulator.add(expression); + } - + } diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/filter/EditVisitor.java index 0000000000,0000000000..8250d3db0a new file mode 100644 --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/EditVisitor.java @@@ -1,0 -1,0 +1,56 @@@ ++/* ++ * 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.internal.filter; ++ ++import org.opengis.filter.FilterFactory; ++ ++/** ++ * Visitor used to copy expressions and filters with same parameterized types. ++ * This class can be used when some filters need to be recreated with the same types ++ * but potentially different values. For example a change of {@link Literal} value ++ * requires to recreate all parents in the filter graph. ++ * ++ * <h2>Partially implemented factory</h2> ++ * {@code EditVisitor} relaxes the usual factory API contract by allowing unsupported factory ++ * methods to return {@code null} instead of throwing an {@link UnsupportedOperationException}. ++ * A null value is interpreted as an instruction to continue to use the old filter or expression, ++ * without replacing it by a new instance created by the {@linkplain #factory}. ++ * By contrast, an {@link UnsupportedOperationException} causes the copy operation to fail. ++ * ++ * @author Johann Sorel (Geomatys) ++ * @author Martin Desruisseaux (Geomatys) ++ * @version 1.4 ++ * ++ * @param <R> the type of resources expected by the filters and expressions. ++ * @param <G> base class of geometry objects. ++ * @param <T> base class of temporal objects. ++ * ++ * @since 1.4 ++ */ ++public class EditVisitor<R,G,T> extends CopyVisitor<R,R,G,T> { ++ /** ++ * Creates a new edit visitor with given factory. ++ * If the {@code force} argument is {@code false}, then the factory is used for ++ * creating new filters and expressions only when at least one operand changed. ++ * ++ * @param factory the factory to use for creating the new filters and expressions. ++ * @param force whether to force new filters or expressions even when existing instances could be reused. ++ */ ++ public EditVisitor(final FilterFactory<R,G,T> factory, final boolean force) { ++ super(factory, force, force); ++ } ++} diff --cc core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java index 67864b3b8d,915385a223..bdc668828a --- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/package-info.java @@@ -20,7 -20,8 +20,7 @@@ * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.3 - * @version 1.1 ++ * @version 1.4 * @since 1.1 - * @module */ package org.apache.sis.internal.filter; diff --cc core/sis-feature/src/test/java/org/apache/sis/internal/filter/CopyVisitorTest.java index 0000000000,5fc1ee494b..9495b3c70f mode 000000,100644..100644 --- a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/CopyVisitorTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/CopyVisitorTest.java @@@ -1,0 -1,403 +1,77 @@@ + /* + * 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.internal.filter; + -import java.time.Instant; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; + import java.util.Map; -import javax.measure.Quantity; -import javax.measure.quantity.Length; -import org.apache.sis.filter.DefaultFilterFactory; ++import org.junit.Test; + import org.apache.sis.test.TestCase; -import org.apache.sis.util.iso.Names; ++import org.apache.sis.filter.DefaultFilterFactory; ++ + import static org.junit.Assert.*; -import org.junit.Test; ++ ++// Branch-dependent imports + import org.opengis.feature.Feature; -import org.opengis.filter.BetweenComparisonOperator; -import org.opengis.filter.BinaryComparisonOperator; -import org.opengis.filter.BinarySpatialOperator; -import org.opengis.filter.DistanceOperator; + import org.opengis.filter.Expression; -import org.opengis.filter.Filter; + import org.opengis.filter.FilterFactory; -import org.opengis.filter.InvalidFilterValueException; -import org.opengis.filter.LikeOperator; -import org.opengis.filter.Literal; -import org.opengis.filter.LogicalOperator; -import org.opengis.filter.MatchAction; -import org.opengis.filter.NilOperator; -import org.opengis.filter.NullOperator; -import org.opengis.filter.ResourceId; -import org.opengis.filter.SortOrder; -import org.opengis.filter.SortProperty; -import org.opengis.filter.TemporalOperator; -import org.opengis.filter.ValueReference; -import org.opengis.filter.Version; -import org.opengis.filter.capability.FilterCapabilities; -import org.opengis.geometry.Envelope; -import org.opengis.metadata.citation.Citation; -import org.opengis.util.ScopedName; ++ + + /** - * Verifies copy from {@link CopyVisitor}. ++ * Tests copies using {@link CopyVisitor}. + * - * @author Johann Sorel (Geomatys) ++ * @author Johann Sorel (Geomatys) ++ * @version 1.4 ++ * @since 1.4 + */ -public class CopyVisitorTest extends TestCase { - ++public final class CopyVisitorTest extends TestCase { + /** - * Test copy a value reference. ++ * Tests copy a value reference. + */ + @Test + public void copyValueReference() { - final FilterFactory<Feature,Object,?> source = DefaultFilterFactory.forFeatures(); - final FilterFactory<Map,Object,Object> target = new MockFactory(); ++ final FilterFactory<Feature, ?, ?> source = DefaultFilterFactory.forFeatures(); ++ final FilterFactory<Map<String,?>, ?, ?> target = new FilterFactoryMock(); ++ final CopyVisitor <Feature, Map<String,?>, ?, ?> visitor = new CopyVisitor<>(target, true); + - final Expression<Feature,?> exp = source.property("name"); - final Expression<Map, Object> result = new CopyVisitor<>(target).copy(exp); ++ final String xpath = "someProperty"; ++ final Expression<Feature, ?> exp = source.property(xpath); ++ final Expression<Map<String,?>, ?> result = visitor.copy(exp); + - assertTrue(result instanceof MockValueReference); ++ assertTrue(result instanceof ValueReferenceMock); ++ assertEquals(xpath, ((ValueReferenceMock) result).getXPath()); + } + + /** - * Test copy a function. ++ * Tests copy of a function. + */ + @Test + public void copyFunction() { - final FilterFactory<Feature,Object,?> source = DefaultFilterFactory.forFeatures(); - final FilterFactory<Map,Object,Object> target = new MockFactory(); - - final Expression<Feature,?> exp1 = source.property("name"); - final Expression<Feature,?> exp2 = source.property("crs"); - final Expression<Feature,?> fct = source.function("ST_GeomFromText", exp1, exp2); - final Expression<Map, Object> result = new CopyVisitor<>(target).copy(fct); - - assertTrue(result instanceof MockFunction); - final MockFunction resultfct = (MockFunction) result; - assertEquals(2, resultfct.getParameters().size()); - assertTrue(resultfct.getParameters().get(0) instanceof MockValueReference); - assertTrue(resultfct.getParameters().get(1) instanceof MockValueReference); - } -} - -final class MockFactory implements FilterFactory<Map, Object, Object>{ - - @Override - public FilterCapabilities getCapabilities() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public ResourceId<Map> resourceId(String rid, Version version, Instant startTime, Instant endTime) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public <V> ValueReference property(String xpath, Class<V> type) { - return new MockValueReference(xpath); - } - - @Override - public <V> Literal<Map, V> literal(V value) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> equal(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> notEqual(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> less(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> greater(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> lessOrEqual(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinaryComparisonOperator<Map> greaterOrEqual(Expression<? super Map, ?> expression1, Expression<? super Map, ?> expression2, boolean isMatchingCase, MatchAction matchAction) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BetweenComparisonOperator<Map> between(Expression<? super Map, ?> expression, Expression<? super Map, ?> lowerBoundary, Expression<? super Map, ?> upperBoundary) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public LikeOperator<Map> like(Expression<? super Map, ?> expression, String pattern, char wildcard, char singleChar, char escape, boolean isMatchingCase) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public NullOperator<Map> isNull(Expression<? super Map, ?> expression) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public NilOperator<Map> isNil(Expression<? super Map, ?> expression, String nilReason) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public LogicalOperator<Map> and(Collection<? extends Filter<? super Map>> operands) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public LogicalOperator<Map> or(Collection<? extends Filter<? super Map>> operands) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public LogicalOperator<Map> not(Filter<? super Map> operand) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> bbox(Expression<? super Map, ? extends Object> geometry, Envelope bounds) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> equals(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } ++ final FilterFactory<Feature, ?, ?> source = DefaultFilterFactory.forFeatures(); ++ final FilterFactory<Map<String,?>, ?, ?> target = new FilterFactoryMock(); ++ final CopyVisitor <Feature, Map<String,?>, ?, ?> visitor = new CopyVisitor<>(target, true); + - @Override - public BinarySpatialOperator<Map> disjoint(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> intersects(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> touches(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> crosses(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> within(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> contains(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public BinarySpatialOperator<Map> overlaps(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public DistanceOperator<Map> beyond(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2, Quantity<Length> distance) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public DistanceOperator<Map> within(Expression<? super Map, ? extends Object> geometry1, Expression<? super Map, ? extends Object> geometry2, Quantity<Length> distance) { - throw new UnsupportedOperationException("Not supported."); - } ++ final Expression<Feature, ?> exp1 = source.property("someProperty"); ++ final Expression<Feature, ?> exp2 = source.property("crs"); ++ final Expression<Feature, ?> fct = source.function("ST_GeomFromText", exp1, exp2); ++ final Expression<Map<String,?>, ?> result = visitor.copy(fct); + - @Override - public TemporalOperator<Map> after(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); ++ assertTrue(result instanceof FunctionMock); ++ var resultfct = ((FunctionMock) result).getParameters(); ++ assertEquals(2, resultfct.size()); ++ assertTrue(resultfct.get(0) instanceof ValueReferenceMock); ++ assertTrue(resultfct.get(1) instanceof ValueReferenceMock); + } - - @Override - public TemporalOperator<Map> before(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> begins(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> begunBy(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> tcontains(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> during(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> tequals(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> toverlaps(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> meets(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> ends(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> overlappedBy(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> metBy(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> endedBy(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public TemporalOperator<Map> anyInteracts(Expression<? super Map, ? extends Object> time1, Expression<? super Map, ? extends Object> time2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Expression<Map, Number> add(Expression<? super Map, ? extends Number> operand1, Expression<? super Map, ? extends Number> operand2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Expression<Map, Number> subtract(Expression<? super Map, ? extends Number> operand1, Expression<? super Map, ? extends Number> operand2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Expression<Map, Number> multiply(Expression<? super Map, ? extends Number> operand1, Expression<? super Map, ? extends Number> operand2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Expression<Map, Number> divide(Expression<? super Map, ? extends Number> operand1, Expression<? super Map, ? extends Number> operand2) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Expression<Map, ?> function(String name, Expression<? super Map, ?>[] parameters) { - return new MockFunction(name, Arrays.asList(parameters)); - } - - @Override - public SortProperty<Map> sort(ValueReference<? super Map, ?> property, SortOrder order) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Citation getVendor() { - throw new UnsupportedOperationException("Not supported."); - } - -} - -final class MockValueReference implements ValueReference<Map, Object> { - - private final String xpath; - - public MockValueReference(String xpath) { - this.xpath = xpath; - } - - @Override - public String getXPath() { - return xpath; - } - - @Override - public Object apply(Map input) throws InvalidFilterValueException { - return input.get(xpath); - } - - @Override - public <N> Expression<Map, N> toValueType(Class<N> target) { - throw new UnsupportedOperationException("Not supported."); - } - -} - -final class MockFunction implements Expression<Map,Object> { - - private final String name; - private final List<Expression<? super Map, ?>> parameters; - - public MockFunction(String name, List<Expression<? super Map, ?>> parameters) { - this.name = name; - this.parameters = parameters; - } - - @Override - public ScopedName getFunctionName() { - return Names.createScopedName(null, null, name); - } - - @Override - public List<Expression<? super Map, ?>> getParameters() { - return parameters; - } - - @Override - public Object apply(Map input) throws InvalidFilterValueException { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public <N> Expression<Map, N> toValueType(Class<N> target) { - throw new UnsupportedOperationException("Not supported."); - } - + } diff --cc core/sis-feature/src/test/java/org/apache/sis/internal/filter/FilterFactoryMock.java index 0000000000,0000000000..195672c98c new file mode 100644 --- /dev/null +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FilterFactoryMock.java @@@ -1,0 -1,0 +1,562 @@@ ++/* ++ * 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.internal.filter; ++ ++import java.time.Instant; ++import java.util.Map; ++import java.util.Arrays; ++import java.util.Collection; ++import javax.measure.Quantity; ++import javax.measure.quantity.Length; ++import org.opengis.geometry.Envelope; ++import org.opengis.metadata.citation.Citation; ++import org.apache.sis.internal.simple.SimpleCitation; ++ ++// Branch-dependent imports ++import org.opengis.filter.*; ++import org.opengis.filter.capability.FilterCapabilities; ++ ++ ++/** ++ * Dummy implementation of filter factory. ++ * The features handled by this implementation are property-value maps. ++ * ++ * @author Johann Sorel (Geomatys) ++ * @version 1.4 ++ * @since 1.4 ++ */ ++final class FilterFactoryMock implements FilterFactory<Map<String,?>, Object, Object> { ++ /** ++ * Creates a new dummy factory. ++ */ ++ FilterFactoryMock() { ++ } ++ ++ /** ++ * Returns the "vendor" responsible for creating this factory implementation. ++ */ ++ @Override ++ public Citation getVendor() { ++ return new SimpleCitation("SIS-tests"); ++ } ++ ++ /** ++ * Creates an expression retrieving the value as an instance of the specified class. ++ */ ++ @Override ++ public <V> ValueReference<Map<String,?>, V> property(String xpath, Class<V> type) { ++ return new ValueReferenceMock<>(xpath, type); ++ } ++ ++ /** ++ * Creates a dummy function with an arbitrary number of parameters. ++ */ ++ @Override ++ public Expression<Map<String,?>, ?> function(String name, Expression<? super Map<String,?>, ?>[] parameters) { ++ return new FunctionMock(name, Arrays.asList(parameters)); ++ } ++ ++ // ======== All operations below this point are unsupported =================================================== ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public FilterCapabilities getCapabilities() { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public ResourceId<Map<String,?>> resourceId(String rid, Version version, Instant startTime, Instant endTime) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public <V> Literal<Map<String,?>, V> literal(V value) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> equal( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> notEqual( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> less( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> greater( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> lessOrEqual( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinaryComparisonOperator<Map<String,?>> greaterOrEqual( ++ Expression<? super Map<String,?>, ?> expression1, ++ Expression<? super Map<String,?>, ?> expression2, ++ boolean isMatchingCase, MatchAction matchAction) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BetweenComparisonOperator<Map<String,?>> between( ++ Expression<? super Map<String,?>, ?> expression, ++ Expression<? super Map<String,?>, ?> lowerBoundary, ++ Expression<? super Map<String,?>, ?> upperBoundary) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public LikeOperator<Map<String,?>> like( ++ Expression<? super Map<String,?>, ?> expression, ++ String pattern, char wildcard, char singleChar, char escape, boolean isMatchingCase) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public NullOperator<Map<String,?>> isNull(Expression<? super Map<String,?>, ?> expression) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public NilOperator<Map<String,?>> isNil(Expression<? super Map<String,?>, ?> expression, String nilReason) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public LogicalOperator<Map<String,?>> and(Collection<? extends Filter<? super Map<String,?>>> operands) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public LogicalOperator<Map<String,?>> or(Collection<? extends Filter<? super Map<String,?>>> operands) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public LogicalOperator<Map<String,?>> not(Filter<? super Map<String,?>> operand) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> bbox( ++ Expression<? super Map<String,?>, ? extends Object> geometry, ++ Envelope bounds) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> equals( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> disjoint( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> intersects( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> touches( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> crosses( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> within( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> contains( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public BinarySpatialOperator<Map<String,?>> overlaps( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public DistanceOperator<Map<String,?>> beyond( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2, ++ Quantity<Length> distance) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public DistanceOperator<Map<String,?>> within( ++ Expression<? super Map<String,?>, ? extends Object> geometry1, ++ Expression<? super Map<String,?>, ? extends Object> geometry2, ++ Quantity<Length> distance) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> after( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> before( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> begins( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> begunBy( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> tcontains( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> during( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> tequals( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> toverlaps( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> meets( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> ends( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> overlappedBy( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> metBy( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> endedBy( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public TemporalOperator<Map<String,?>> anyInteracts( ++ Expression<? super Map<String,?>, ? extends Object> time1, ++ Expression<? super Map<String,?>, ? extends Object> time2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public Expression<Map<String,?>, Number> add( ++ Expression<? super Map<String,?>, ? extends Number> operand1, ++ Expression<? super Map<String,?>, ? extends Number> operand2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public Expression<Map<String,?>, Number> subtract( ++ Expression<? super Map<String,?>, ? extends Number> operand1, ++ Expression<? super Map<String,?>, ? extends Number> operand2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public Expression<Map<String,?>, Number> multiply( ++ Expression<? super Map<String,?>, ? extends Number> operand1, ++ Expression<? super Map<String,?>, ? extends Number> operand2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public Expression<Map<String,?>, Number> divide( ++ Expression<? super Map<String,?>, ? extends Number> operand1, ++ Expression<? super Map<String,?>, ? extends Number> operand2) ++ { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public SortProperty<Map<String,?>> sort(ValueReference<? super Map<String,?>, ?> property, SortOrder order) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++} diff --cc core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java index 0000000000,0000000000..cae63d9653 new file mode 100644 --- /dev/null +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/FunctionMock.java @@@ -1,0 -1,0 +1,91 @@@ ++/* ++ * 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.internal.filter; ++ ++import java.util.Map; ++import java.util.List; ++import org.opengis.util.ScopedName; ++import org.apache.sis.util.iso.Names; ++ ++// Branch-dependent imports ++import org.opengis.filter.Expression; ++ ++ ++/** ++ * Dummy implementation of filter function. ++ * The features handled by this implementation are property-value maps. ++ * This class stores a function name and parameters but cannot do any operation. ++ * ++ * @author Johann Sorel (Geomatys) ++ * @version 1.4 ++ * @since 1.4 ++ */ ++final class FunctionMock implements Expression<Map<String,?>, Object> { ++ /** ++ * The local part of the function name. ++ */ ++ private final String name; ++ ++ /** ++ * The function parameters. ++ */ ++ private final List<Expression<? super Map<String,?>, ?>> parameters; ++ ++ /** ++ * Creates a new dummy function. ++ * ++ * @param name the local part of the function name. ++ * @param parameters the function parameters. ++ */ ++ FunctionMock(final String name, final List<Expression<? super Map<String,?>, ?>> parameters) { ++ this.name = name; ++ this.parameters = parameters; ++ } ++ ++ /** ++ * Returns the function name. ++ */ ++ @Override ++ public ScopedName getFunctionName() { ++ return Names.createScopedName(null, null, name); ++ } ++ ++ /** ++ * Returns the function parameters. ++ */ ++ @Override ++ @SuppressWarnings("ReturnOfCollectionOrArrayField") ++ public List<Expression<? super Map<String,?>, ?>> getParameters() { ++ return parameters; ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public Object apply(Map<String,?> feature) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++ ++ /** ++ * Unsupported operation. ++ */ ++ @Override ++ public <N> Expression<Map<String,?>, N> toValueType(Class<N> target) { ++ throw new UnsupportedOperationException("Not supported."); ++ } ++} diff --cc core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java index 0000000000,0000000000..90eb9a1f75 new file mode 100644 --- /dev/null +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/ValueReferenceMock.java @@@ -1,0 -1,0 +1,82 @@@ ++/* ++ * 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.internal.filter; ++ ++import java.util.Map; ++ ++// Branch-dependent imports ++import org.opengis.filter.Expression; ++import org.opengis.filter.ValueReference; ++ ++ ++/** ++ * Dummy implementation of property value reference. ++ * The features handled by this implementation are property-value maps. ++ * ++ * @author Johann Sorel (Geomatys) ++ * @version 1.4 ++ * ++ * @param <V> type of values returned by this expression. ++ * ++ * @since 1.4 ++ */ ++final class ValueReferenceMock<V> implements ValueReference<Map<String,?>, V> { ++ /** ++ * Name of the property for which to get values. ++ */ ++ private final String xpath; ++ ++ /** ++ * Expected type of values. ++ */ ++ private final Class<V> type; ++ ++ /** ++ * Creates a new dummy reference. ++ * ++ * @param xpath name of the property for which to get values. ++ * @param type type of values returned by this expression. ++ */ ++ ValueReferenceMock(final String xpath, final Class<V> type) { ++ this.xpath = xpath; ++ this.type = type; ++ } ++ ++ /** ++ * Returns the name of the property for which to get values. ++ */ ++ @Override ++ public String getXPath() { ++ return xpath; ++ } ++ ++ /** ++ * Returns the value of the referenced property in the given feature. ++ */ ++ @Override ++ public V apply(final Map<String,?> feature) { ++ return type.cast(feature.get(xpath)); ++ } ++ ++ /** ++ * Returns a reference to the same value but casted to a different type. ++ */ ++ @Override ++ public <N> Expression<Map<String,?>, N> toValueType(final Class<N> target) { ++ return new ValueReferenceMock<>(xpath, target); ++ } ++} diff --cc core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java index 2c588d0a13,16a86d5a21..aa285fcdcf --- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java +++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java @@@ -26,8 -26,9 +26,8 @@@ import org.junit.runners.Suite * * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.3 - * @version 1.2 ++ * @version 1.4 * @since 0.5 - * @module */ @Suite.SuiteClasses({ org.apache.sis.feature.DefaultAttributeTypeTest.class,