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 a206c7bd933939d81820e89703ec65590c5334ff Author: Martin Desruisseaux <[email protected]> AuthorDate: Mon Aug 26 19:27:32 2019 +0200 Hide SQLMM class from public API (the function that it provides are available through DefaultFilterFactory) and refactor tests. --- .../apache/sis/filter/DefaultFilterFactory.java | 234 ++++++++++++--------- .../src/main/java/org/apache/sis/filter/SQLMM.java | 53 ++--- .../sis/internal/feature/FunctionRegister.java | 39 ++-- .../org/apache/sis/internal/feature/Resources.java | 5 + .../sis/internal/feature/Resources.properties | 1 + .../sis/internal/feature/Resources_fr.properties | 1 + ...rg.apache.sis.internal.feature.FunctionRegister | 3 - .../test/java/org/apache/sis/filter/SQLMMTest.java | 234 ++++++++++++--------- 8 files changed, 322 insertions(+), 248 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java index 94a1cb4..018b129 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java @@ -18,12 +18,10 @@ package org.apache.sis.filter; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; -import org.apache.sis.internal.feature.FunctionRegister; import org.opengis.filter.*; import org.opengis.filter.capability.*; import org.opengis.filter.capability.SpatialOperator; @@ -35,6 +33,10 @@ import org.opengis.filter.temporal.*; import org.opengis.geometry.Envelope; import org.opengis.geometry.Geometry; import org.opengis.util.GenericName; +import org.apache.sis.internal.system.Modules; +import org.apache.sis.internal.system.SystemListener; +import org.apache.sis.internal.feature.FunctionRegister; +import org.apache.sis.internal.feature.Resources; /** @@ -51,21 +53,25 @@ import org.opengis.util.GenericName; * @module */ public class DefaultFilterFactory implements FilterFactory2 { - - //TODO we should use a class like FeatureNaming to store and find registers. - //but it is not accessible here, should we move it ? - private static Map<String,FunctionRegister> FUNCTION_REGISTERS = new HashMap<>(); + /** + * All functions identified by a name like {@code "cos"}, {@code "hypot"}, <i>etc</i>. + * The actual function creations is delegated to an external factory such as {@link SQLMM}. + * The factories are fetched by {@link #function(String, Expression...)} when first needed. + * This factory is cleared if classpath changes, for allowing dynamic reloading. + * + * @see #function(String, Expression...) + */ + private static final Map<String,FunctionRegister> FUNCTION_REGISTERS = new HashMap<>(); static { - final Iterator<FunctionRegister> iterator = ServiceLoader.load(FunctionRegister.class).iterator(); - while (iterator.hasNext()) { - FunctionRegister register = iterator.next(); - for (String name : register.getNames()) { - FUNCTION_REGISTERS.put(name, register); + SystemListener.add(new SystemListener(Modules.FEATURE) { + @Override protected void classpathChanged() { + synchronized (FUNCTION_REGISTERS) { + FUNCTION_REGISTERS.clear(); + } } - } + }); } - /** * Creates a new factory. */ @@ -97,7 +103,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public BBOX bbox(final Expression e, final double minx, final double miny, @@ -107,7 +113,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public BBOX bbox(final Expression e, final Envelope bounds) { @@ -135,7 +141,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Beyond beyond(final Expression left, final Expression right, @@ -145,7 +151,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Contains contains(final String propertyName, final Geometry geometry) { @@ -155,7 +161,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Contains contains(final Expression left, final Expression right) { @@ -163,7 +169,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Crosses crosses(final String propertyName, final Geometry geometry) { @@ -173,7 +179,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Crosses crosses(final Expression left, final Expression right) { @@ -181,7 +187,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Disjoint disjoint(final String propertyName, final Geometry geometry) { @@ -191,7 +197,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Disjoint disjoint(final Expression left, final Expression right) { @@ -199,7 +205,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public DWithin dwithin(final String propertyName, final Geometry geometry, @@ -211,7 +217,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public DWithin dwithin(final Expression left, final Expression right, @@ -221,7 +227,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Equals equals(final String propertyName, final Geometry geometry) { @@ -231,7 +237,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Equals equal(final Expression left, final Expression right) { @@ -239,7 +245,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Intersects intersects(final String propertyName, final Geometry geometry) { @@ -249,7 +255,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Intersects intersects(final Expression left, final Expression right) { @@ -257,7 +263,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Overlaps overlaps(final String propertyName, final Geometry geometry) { @@ -267,7 +273,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Overlaps overlaps(final Expression left, final Expression right) { @@ -275,7 +281,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Touches touches(final String propertyName, final Geometry geometry) { @@ -285,7 +291,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Touches touches(final Expression left, final Expression right) { @@ -293,7 +299,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Within within(final String propertyName, final Geometry geometry) { @@ -303,7 +309,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Within within(final Expression left, final Expression right) { @@ -313,7 +319,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // IDENTIFIERS ///////////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public FeatureId featureId(final String id) { @@ -321,7 +327,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public GmlObjectId gmlObjectId(final String id) { @@ -331,7 +337,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // FILTERS ///////////////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public And and(final Filter filter1, final Filter filter2) { @@ -339,7 +345,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public And and(final List<Filter> filters) { @@ -347,7 +353,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Or or(final Filter filter1, final Filter filter2) { @@ -355,7 +361,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Or or(final List<Filter> filters) { @@ -363,7 +369,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Not not(final Filter filter) { @@ -371,7 +377,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Id id(final Set<? extends Identifier> ids) { @@ -379,7 +385,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyName property(final GenericName name) { @@ -397,7 +403,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsBetween between(final Expression expression, final Expression lower, final Expression upper) { @@ -405,7 +411,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsEqualTo equals(final Expression expression1, final Expression expression2) { @@ -413,7 +419,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsEqualTo equal(final Expression expression1, final Expression expression2, @@ -423,7 +429,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsNotEqualTo notEqual(final Expression expression1, final Expression expression2) { @@ -431,7 +437,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsNotEqualTo notEqual(final Expression expression1, final Expression expression2, @@ -441,7 +447,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsGreaterThan greater(final Expression expression1, final Expression expression2) { @@ -449,7 +455,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsGreaterThan greater(final Expression expression1, final Expression expression2, @@ -459,7 +465,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsGreaterThanOrEqualTo greaterOrEqual(final Expression expression1, final Expression expression2) { @@ -467,7 +473,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsGreaterThanOrEqualTo greaterOrEqual(final Expression expression1, final Expression expression2, @@ -477,7 +483,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLessThan less(final Expression expression1, final Expression expression2) { @@ -485,7 +491,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLessThan less(final Expression expression1, final Expression expression2, @@ -495,7 +501,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLessThanOrEqualTo lessOrEqual(final Expression expression1, final Expression expression2) { @@ -503,7 +509,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLessThanOrEqualTo lessOrEqual(final Expression expression1, final Expression expression2, @@ -513,7 +519,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLike like(final Expression expression, final String pattern) { @@ -521,7 +527,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLike like(final Expression expression, final String pattern, @@ -531,7 +537,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsLike like(final Expression expression, final String pattern, @@ -542,7 +548,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsNull isNull(final Expression expression) { @@ -550,7 +556,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public PropertyIsNil isNil(Expression expression) { @@ -560,7 +566,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // TEMPORAL FILTER ///////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public After after(Expression expression1, Expression expression2) { @@ -568,7 +574,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public AnyInteracts anyInteracts(Expression expression1, Expression expression2) { @@ -576,7 +582,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Before before(Expression expression1, Expression expression2) { @@ -584,7 +590,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Begins begins(Expression expression1, Expression expression2) { @@ -592,7 +598,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public BegunBy begunBy(Expression expression1, Expression expression2) { @@ -600,7 +606,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public During during(Expression expression1, Expression expression2) { @@ -608,7 +614,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Ends ends(Expression expression1, Expression expression2) { @@ -616,7 +622,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public EndedBy endedBy(Expression expression1, Expression expression2) { @@ -624,7 +630,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Meets meets(Expression expression1, Expression expression2) { @@ -632,7 +638,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public MetBy metBy(Expression expression1, Expression expression2) { @@ -640,7 +646,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public OverlappedBy overlappedBy(Expression expression1, Expression expression2) { @@ -648,7 +654,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public TContains tcontains(Expression expression1, Expression expression2) { @@ -656,7 +662,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public TEquals tequals(Expression expression1, Expression expression2) { @@ -664,7 +670,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public TOverlaps toverlaps(Expression expression1, Expression expression2) { @@ -674,7 +680,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // EXPRESSIONS ///////////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Add add(final Expression expression1, final Expression expression2) { @@ -682,7 +688,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Divide divide(final Expression expression1, final Expression expression2) { @@ -690,7 +696,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Multiply multiply(final Expression expression1, final Expression expression2) { @@ -698,7 +704,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Subtract subtract(final Expression expression1, final Expression expression2) { @@ -706,19 +712,39 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Function function(final String name, final Expression... parameters) { - final FunctionRegister register = FUNCTION_REGISTERS.get(name); + final FunctionRegister register; + synchronized (FUNCTION_REGISTERS) { + if (FUNCTION_REGISTERS.isEmpty()) { + /* + * Load functions when first needed or if classpath changed since last invocation. + * The SQLMM factory is hard-coded because it is considered as a basic service to + * be provided by all DefaultFilterFactory implementations, and for avoiding the + * need to make SQLMM class public. + */ + final SQLMM r = new SQLMM(); + for (final String fn : r.getNames()) { + FUNCTION_REGISTERS.put(fn, r); + } + for (final FunctionRegister er : ServiceLoader.load(FunctionRegister.class)) { + for (final String fn : er.getNames()) { + FUNCTION_REGISTERS.putIfAbsent(fn, er); + } + } + } + register = FUNCTION_REGISTERS.get(name); + } if (register == null) { - throw new IllegalArgumentException("Unknown function "+name); + throw new IllegalArgumentException(Resources.format(Resources.Keys.UnknownFunction_1, name)); } return register.create(name, parameters); } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final Object value) { @@ -726,7 +752,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final byte value) { @@ -734,7 +760,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final short value) { @@ -742,7 +768,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final int value) { @@ -750,7 +776,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final long value) { @@ -758,7 +784,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final float value) { @@ -766,7 +792,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final double value) { @@ -774,7 +800,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final char value) { @@ -782,7 +808,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Literal literal(final boolean value) { @@ -792,7 +818,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // SORT BY ///////////////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public SortBy sort(final String propertyName, final SortOrder order) { @@ -802,7 +828,7 @@ public class DefaultFilterFactory implements FilterFactory2 { // CAPABILITIES //////////////////////////////////////////////////////////// /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Operator operator(final String name) { @@ -810,7 +836,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public SpatialOperator spatialOperator(final String name, final GeometryOperand[] geometryOperands) { @@ -818,7 +844,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public FunctionName functionName(final String name, final int nargs) { @@ -826,7 +852,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public Functions functions(final FunctionName[] functionNames) { @@ -834,7 +860,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public SpatialOperators spatialOperators(final SpatialOperator[] spatialOperators) { @@ -842,7 +868,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public ComparisonOperators comparisonOperators(final Operator[] comparisonOperators) { @@ -850,7 +876,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public ArithmeticOperators arithmeticOperators(final boolean simple, final Functions functions) { @@ -858,7 +884,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public ScalarCapabilities scalarCapabilities(final ComparisonOperators comparison, @@ -868,7 +894,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public SpatialCapabilities spatialCapabilities( @@ -878,7 +904,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public IdCapabilities idCapabilities(final boolean eid, final boolean fid) { @@ -886,7 +912,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public FilterCapabilities capabilities(final String version, @@ -897,7 +923,7 @@ public class DefaultFilterFactory implements FilterFactory2 { } /** - * {@inheritDoc } + * {@inheritDoc} */ @Override public TemporalCapabilities temporalCapabilities(TemporalOperand[] temporalOperands, TemporalOperators temporal) { diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java b/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java index 86d3196..b32d3dc 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java +++ b/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java @@ -16,20 +16,21 @@ */ package org.apache.sis.filter; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.apache.sis.internal.feature.FunctionRegister; -import org.apache.sis.util.ArgumentChecks; +import java.util.Arrays; +import java.util.Collection; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Function; import org.opengis.util.FactoryException; +import org.apache.sis.internal.feature.FunctionRegister; +import org.apache.sis.internal.feature.Resources; +import org.apache.sis.util.ArgumentChecks; /** * A register of functions defined by the SQL/MM standard. + * This standard is defined by <a href="https://www.iso.org/standard/60343.html">ISO/IEC 13249-3:2016 + * Information technology — Database languages — SQL multimedia and application packages — Part 3: Spatial</a>. * - * @todo Hide from public API. * @todo Implement all SQL/MM specification functions. * * @author Johann Sorel (Geomatys) @@ -37,37 +38,39 @@ import org.opengis.util.FactoryException; * @since 1.0 * @module */ -public final class SQLMM implements FunctionRegister { +final class SQLMM implements FunctionRegister { /** - * Names of all functions known to this register. + * Creates the default register. */ - private static final Set<String> FUNCTIONS; - static { - Set<String> names = new HashSet<>(); - names.add(ST_Transform.NAME); - names.add(ST_Centroid.NAME); - names.add(ST_Buffer.NAME); - FUNCTIONS = Collections.unmodifiableSet(names); + SQLMM() { } /** - * Creates the default register. + * Returns a unique name for this factory. */ - public SQLMM() { - } - @Override public String getIdentifier() { return "SQL/MM"; } + /** + * Returns the names of all functions known to this register. + */ @Override - public Set<String> getNames() { - return FUNCTIONS; + public Collection<String> getNames() { + return Arrays.asList(ST_Transform.NAME, ST_Centroid.NAME, ST_Buffer.NAME); } + /** + * Create a new function of the given name with given parameters. + * + * @param name name of the function to create. + * @param parameters function parameters. + * @return function for the given name and parameters. + * @throws IllegalArgumentException if function name is unknown or some parameters are illegal. + */ @Override - public Function create(final String name, Expression... parameters) { + public Function create(final String name, Expression[] parameters) { ArgumentChecks.ensureNonNull("name", name); ArgumentChecks.ensureNonNull("parameters", parameters); parameters = parameters.clone(); @@ -77,9 +80,9 @@ public final class SQLMM implements FunctionRegister { try { switch (name) { case ST_Transform.NAME: return new ST_Transform(parameters); - case ST_Centroid.NAME: return new ST_Centroid(parameters); - case ST_Buffer.NAME: return new ST_Buffer(parameters); - default: throw new IllegalArgumentException("Unknown function " + name); + case ST_Centroid.NAME: return new ST_Centroid(parameters); + case ST_Buffer.NAME: return new ST_Buffer(parameters); + default: throw new IllegalArgumentException(Resources.format(Resources.Keys.UnknownFunction_1, name)); } } catch (FactoryException e) { throw new IllegalArgumentException(e); diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java index 343e3c3..b4af7b3 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java @@ -16,38 +16,53 @@ */ package org.apache.sis.internal.feature; -import java.util.Set; +import java.util.Collection; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Function; /** + * A factory of {@link org.opengis.filter} functions identified by their names. + * Each factory can provide an arbitrary number of functions, enumerated by {@link #getNames()}. + * The {@link org.apache.sis.filter.DefaultFilterFactory#function(String, Expression...)} method + * delegates to this interface for creating the function implementation for a given name. * - * @author Johann Sorel (Geomatys) + * <p><b>Warning:</b> there is currently no mechanism for avoiding name collision. + * It is implementer responsibility to keep trace of the whole universe of functions and avoid collision. + * This interface is hidden in internal API (for now) for that reason, and also because the API may change + * in any future Apache SIS version.</p> + * + * @author Johann Sorel (Geomatys) + * @version 1.0 + * @since 1.0 + * @module + * + * @see org.opengis.filter.FilterFactory#function(String, Expression...) */ public interface FunctionRegister { /** - * - * Note : should it be the starting element of all function names to avoid any conflict ? + * Returns a unique name for this factory. * * @return factory unique name. */ String getIdentifier(); /** - * Note : should we use GenericName ? + * Returns the names of all functions that this factory can create. + * It is currently implementer responsibility to ensure that there is no name collision with + * functions provided by other factories (this problem may be improved in future SIS release). * * @return set of supported function names. */ - Set<String> getNames(); + Collection<String> getNames(); /** - * Create a new function with given parameters. + * Create a new function of the given name with given parameters. * - * @param name function name, not null - * @param parameters function parameters - * @return - * @throws IllegalArgumentException if function name is unknown or parameters do not match + * @param name name of the function to create (not null). + * @param parameters function parameters. + * @return function for the given name and parameters. + * @throws IllegalArgumentException if function name is unknown or some parameters are illegal. */ - Function create(String name, Expression... parameters) throws IllegalArgumentException; + Function create(String name, Expression[] parameters) throws IllegalArgumentException; } diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java index 44d8258..76ef8c3 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java @@ -320,6 +320,11 @@ public final class Resources extends IndexedResourceBundle { public static final short UnknownDataType_1 = 50; /** + * Function “{0}” is unknown or unsupported. + */ + public static final short UnknownFunction_1 = 58; + + /** * Feature named “{0}” has not yet been resolved. */ public static final short UnresolvedFeatureName_1 = 18; diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties index b63161e..8ae3d8a 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties @@ -69,6 +69,7 @@ UnexpectedNumberOfBands_2 = Expected {0} bands but got {1}. UnexpectedNumberOfComponents_4 = The \u201c{1}\u201d value given to \u201c{0}\u201d property should be separable in {2} components, but we got {3}. UnexpectedNumberOfCoordinates_4 = The \u201c{0}\u201d feature at {1} has a {3} coordinate values, while we expected a multiple of {2}. UnknownDataType_1 = Raster data type \u2018{0}\u2019 is unknown or unsupported. +UnknownFunction_1 = Function \u201c{0}\u201d is unknown or unsupported. UnresolvedFeatureName_1 = Feature named \u201c{0}\u201d has not yet been resolved. UnspecifiedCRS = Coordinate reference system is unspecified. UnspecifiedGridExtent = Grid extent is unspecified. diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties index dbfee3f..3828cfa 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties @@ -75,6 +75,7 @@ UnexpectedNumberOfBands_2 = On attendait {0} bandes mais {1} ont \u00e9t UnexpectedNumberOfComponents_4 = La valeur \u00ab\u202f{1}\u202f\u00bb donn\u00e9e \u00e0 la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb devrait \u00eatre s\u00e9parable en {2} composantes, mais on en a obtenus {3}. UnexpectedNumberOfCoordinates_4 = L\u2019entit\u00e9 nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb \u00e0 {1} contient {3} coordonn\u00e9es, alors qu\u2019on attendait un multiple de {2}. UnknownDataType_1 = Le type de donn\u00e9es raster \u2018{0}\u2019 est inconnu ou non-support\u00e9. +UnknownFunction_1 = La fonction \u00ab\u202f{0}\u202f\u00bb est inconnue ou non-support\u00e9e. UnresolvedFeatureName_1 = L\u2019entit\u00e9 nomm\u00e9e \u00ab\u202f{0}\u202f\u00bb n\u2019a pas encore \u00e9t\u00e9 r\u00e9solue. UnspecifiedCRS = Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9. UnspecifiedGridExtent = L\u2019\u00e9tendue de la grille n\u2019a pas \u00e9t\u00e9 sp\u00e9cifi\u00e9e. diff --git a/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister b/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister deleted file mode 100644 index d2b068f..0000000 --- a/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister +++ /dev/null @@ -1,3 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; -# and to You under the Apache License, Version 2.0. -org.apache.sis.filter.SQLMM diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java index 89e9660..2e98852 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java @@ -16,159 +16,185 @@ */ package org.apache.sis.filter; -import org.apache.sis.feature.builder.FeatureTypeBuilder; -import org.apache.sis.referencing.CommonCRS; -import org.apache.sis.test.TestCase; -import org.junit.Assert; -import org.junit.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; -import org.opengis.filter.FilterFactory2; +import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.Function; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.apache.sis.feature.builder.FeatureTypeBuilder; +import org.apache.sis.referencing.crs.HardCodedCRS; +import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.test.TestCase; +import org.junit.Test; + +import static org.opengis.test.Assert.*; /** * Tests {@link SQLMM} functions implementations. + * Current implementation tests Java Topology Suite (JTS) implementation only. * - * @author Johann Sorel (Geomatys) + * @author Johann Sorel (Geomatys) * @version 1.0 * @since 1.0 * @module */ -public class SQLMMTest extends TestCase { +public final strictfp class SQLMMTest extends TestCase { /** * The factory to use for creating the objects to test. */ - private final FilterFactory2 factory = new DefaultFilterFactory(); - private final GeometryFactory gf = new GeometryFactory(); + private final FilterFactory factory; /** - * Test SQL/MM ST_Transform function. + * The factory to use for creating Java Topology Suite (JTS) objects. */ - @Test - public void ST_TransformTest() { + private final GeometryFactory geometryFactory; - CoordinateReferenceSystem inCrs = CommonCRS.WGS84.normalizedGeographic(); - CoordinateReferenceSystem outCrs = CommonCRS.WGS84.geographic(); + /** + * Creates a new test case. + */ + public SQLMMTest() { + factory = new DefaultFilterFactory(); + geometryFactory = new GeometryFactory(); + } - //test invalid + /** + * Verifies that attempts to create a function of the given name fail if no argument is provided. + */ + private void assertRequireArguments(final String functionName) { try { - factory.function("ST_Transform"); - Assert.fail("Creation with no argument should fail"); + factory.function(functionName); + fail("Creation with no argument should fail"); } catch (IllegalArgumentException ex) { - //ok + final String message = ex.getMessage(); + assertTrue(message, message.contains("parameters")); } + } - //create a test feature - final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); - ftb.setName("test"); - ftb.addAttribute(Point.class).setName("geom").setCRS(inCrs); + /** + * Wraps the given geometry in a feature object. The geometry will be stored in a property named {@code "geom"}. + * + * @param geometry the geometry to wrap in a feature. + * @param crs the coordinate reference system to assign to the geometry. + */ + private static Feature wrapInFeature(final Geometry geometry, final CoordinateReferenceSystem crs) { + final FeatureTypeBuilder ftb = new FeatureTypeBuilder().setName("test"); + ftb.addAttribute(Point.class).setName("geom").setCRS(crs); final FeatureType type = ftb.build(); - final Point geometry = gf.createPoint(new Coordinate(10, 30)); - geometry.setUserData(inCrs); + geometry.setUserData(crs); final Feature feature = type.newInstance(); feature.setPropertyValue("geom", geometry); + return feature; + } - { //test transform function using epsg code - final Function fct = factory.function("ST_Transform", factory.property("geom"), factory.literal("EPSG:4326")); - - //check result - final Object newGeom = fct.evaluate(feature); - Assert.assertTrue(newGeom instanceof Point); - final Point trs = (Point) newGeom; - Assert.assertEquals(outCrs, trs.getUserData()); - Assert.assertEquals(30.0, trs.getX(), 0.0); - Assert.assertEquals(10.0, trs.getY(), 0.0); - } - - { //test transform function using crs object - final Function fct = factory.function("ST_Transform", factory.property("geom"), factory.literal(outCrs)); - - //check result - final Object newGeom = fct.evaluate(feature); - Assert.assertTrue(newGeom instanceof Point); - final Point trs = (Point) newGeom; - Assert.assertEquals(outCrs, trs.getUserData()); - Assert.assertEquals(30.0, trs.getX(), 0.0); - Assert.assertEquals(10.0, trs.getY(), 0.0); - } + /** + * Evaluates the given function and returns its result as an object of the given type. + * + * @param expectedType the expected type of the result. + * @param feature the feature to use as input value. May be {@code null}. + * @param testing the function to test. + * @return evaluation result. + */ + private static <G extends Geometry> G evaluate(final Class<G> expectedType, final Feature feature, final Function testing) { + final Object result = testing.evaluate(feature); + assertInstanceOf("Expected JTS geometry.", expectedType, result); + return expectedType.cast(result); } /** - * Test SQL/MM ST_Centroid function. + * Test SQL/MM {@link ST_Transform} function. */ @Test - public void ST_CentroidTest() { - - CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic(); + public void testTransform() { + /* + * Verify that creation of a function without arguments is not allowed. + */ + assertRequireArguments("ST_Transform"); + /* + * Create a feature to be used for testing purpose. For this test, the CRS transformation + * will be simply a change of axis order from (λ,φ) to (φ,λ). + */ + final Point geometry = geometryFactory.createPoint(new Coordinate(10, 30)); + final Feature feature = wrapInFeature(geometry, HardCodedCRS.WGS84); + /* + * Test transform function using the full CRS object, then using only EPSG code. + */ + testTransform(feature, HardCodedCRS.WGS84_φλ, HardCodedCRS.WGS84_φλ); + testTransform(feature, "EPSG:4326", CommonCRS.WGS84.geographic()); + } - //test invalid - try { - factory.function("ST_Centroid"); - Assert.fail("Creation with no argument should fail"); - } catch (IllegalArgumentException ex) { - //ok - } + /** + * Tests {@link ST_Transform} on the given feature. The feature must have a property named {@code "geom"}. + * The result is expected to be a geometry using WGS84 datum with (φ,λ) axis order. + * + * @param feature the feature to use for testing the function. + * @param specifiedCRS the argument to give to the {@code "ST_Transform"} function. + * @param expectedCRS the CRS expected as a result of the transform function. + */ + private void testTransform(final Feature feature, final Object specifiedCRS, final CoordinateReferenceSystem expectedCRS) { + final Point result = evaluate(Point.class, feature, factory.function("ST_Transform", + factory.property("geom"), factory.literal(specifiedCRS))); + assertEquals("userData", expectedCRS, result.getUserData()); + assertEquals(30, result.getX(), STRICT); + assertEquals(10, result.getY(), STRICT); + } - final LineString geometry = gf.createLineString(new Coordinate[]{new Coordinate(10, 20), new Coordinate(30, 20)}); + /** + * Test SQL/MM {@link ST_Centroid} function. + */ + @Test + public void testCentroid() { + assertRequireArguments("ST_Centroid"); + /* + * Creates a single linestring for testing the centroid function. The CRS is not used by this computation, + * but we declare it in order to verify that the information is propagated to the result. + */ + final LineString geometry = geometryFactory.createLineString(new Coordinate[] { + new Coordinate(10, 20), + new Coordinate(30, 20) + }); geometry.setSRID(4326); - geometry.setUserData(crs); - - - final Function fct = factory.function("ST_Centroid", factory.literal(geometry)); - - //check result - final Object newGeom = fct.evaluate(null); - Assert.assertTrue(newGeom instanceof Point); - final Point trs = (Point) newGeom; - Assert.assertEquals(crs, trs.getUserData()); - Assert.assertEquals(4326, trs.getSRID()); - Assert.assertEquals(20.0, trs.getX(), 0.0); - Assert.assertEquals(20.0, trs.getY(), 0.0); - + geometry.setUserData(HardCodedCRS.WGS84_φλ); + /* + * Execute the function and check the result. + */ + final Point result = evaluate(Point.class, null, factory.function("ST_Centroid", factory.literal(geometry))); + assertEquals("userData", HardCodedCRS.WGS84_φλ, result.getUserData()); + assertEquals("SRID", 4326, result.getSRID()); + assertEquals(20, result.getX(), STRICT); + assertEquals(20, result.getY(), STRICT); } /** - * Test SQL/MM ST_Buffer function. + * Test SQL/MM {@link ST_Buffer} function. */ @Test public void ST_BufferTest() { - - CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic(); - - //test invalid - try { - factory.function("ST_Buffer"); - Assert.fail("Creation with no argument should fail"); - } catch (IllegalArgumentException ex) { - //ok - } - - final Point geometry = gf.createPoint(new Coordinate(10, 20)); + assertRequireArguments("ST_Buffer"); + /* + * Creates a single point for testing the buffer function. The CRS is not used by this computation, + * but we declare it in order to verify that the information is propagated to the result. + */ + final Point geometry = geometryFactory.createPoint(new Coordinate(10, 20)); + geometry.setUserData(HardCodedCRS.WGS84_φλ); geometry.setSRID(4326); - geometry.setUserData(crs); - - - final Function fct = factory.function("ST_Buffer", factory.literal(geometry), factory.literal(1.0)); - - //check result - final Object newGeom = fct.evaluate(null); - Assert.assertTrue(newGeom instanceof Polygon); - final Polygon trs = (Polygon) newGeom; - Assert.assertEquals(crs, trs.getUserData()); - Assert.assertEquals(4326, trs.getSRID()); - Envelope env = trs.getEnvelopeInternal(); - Assert.assertEquals(9.0, env.getMinX(), 0.0); - Assert.assertEquals(11.0, env.getMaxX(), 0.0); - Assert.assertEquals(19.0, env.getMinY(), 0.0); - Assert.assertEquals(21.0, env.getMaxY(), 0.0); - + /* + * Execute the function and check the result. + */ + final Polygon result = evaluate(Polygon.class, null, factory.function("ST_Buffer", factory.literal(geometry), factory.literal(1))); + assertEquals("userData", HardCodedCRS.WGS84_φλ, result.getUserData()); + assertEquals("SRID", 4326, result.getSRID()); + final Envelope env = result.getEnvelopeInternal(); + assertEquals( 9, env.getMinX(), STRICT); + assertEquals(11, env.getMaxX(), STRICT); + assertEquals(19, env.getMinY(), STRICT); + assertEquals(21, env.getMaxY(), STRICT); } }
