You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/08/26 17:28:16 UTC

[sis] branch geoapi-4.0 updated (b4d7b41 -> a206c7b)

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

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


    from b4d7b41  More tests about temporal functions.
     new 5961fbc  Verify also the ComparisonFunction name during tests, and verify at least one of the two expression operands. Recycle some previously created objects, and add comments about what is being tested in TemporalFunctionTest.
     new 25a4935  Minor reformating and test refactoring. Some module of CQL reviews will be done later (hopefully), among other for avoiding dependency to AntLR, delegate geometry parsing to JTS/ESRI library and for trying to avoid direct dependency to JTS. In the meantime, the module is available on geoapi=4.0 branch for experiment but will be excluded from Apache SIS 1.0 release.
     new a206c7b  Hide SQLMM class from public API (the function that it provides are available through DefaultFilterFactory) and refactor tests.

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


Summary of changes:
 core/sis-cql/nb-configuration.xml                  |  18 --
 core/sis-cql/pom.xml                               |  40 +---
 .../src/main/java/org/apache/sis/cql/CQL.java      |  28 +--
 .../main/java/org/apache/sis/cql/CQLException.java |  20 +-
 .../org/apache/sis/cql/FilterToCQLVisitor.java     |  13 +-
 .../java/org/apache/sis/internal/cql/AntlrCQL.java |  22 +-
 .../{TemporalUtilities.java => CQLTestCase.java}   |  32 ++-
 .../test/java/org/apache/sis/cql/CQLTestSuite.java |  11 +-
 .../org/apache/sis/cql/ExpressionReadingTest.java  |  97 ++++-----
 .../org/apache/sis/cql/ExpressionWritingTest.java  |  65 +++---
 .../java/org/apache/sis/cql/FilterReadingTest.java |  45 ++--
 .../java/org/apache/sis/cql/FilterWritingTest.java |  45 ++--
 .../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 -
 .../apache/sis/filter/ComparisonFunctionTest.java  | 101 ++++++---
 .../test/java/org/apache/sis/filter/SQLMMTest.java | 234 ++++++++++++---------
 .../apache/sis/filter/TemporalFunctionTest.java    |  58 ++++-
 22 files changed, 649 insertions(+), 516 deletions(-)
 delete mode 100644 core/sis-cql/nb-configuration.xml
 rename core/sis-cql/src/test/java/org/apache/sis/cql/{TemporalUtilities.java => CQLTestCase.java} (53%)
 delete mode 100644 core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister


[sis] 02/03: Minor reformating and test refactoring. Some module of CQL reviews will be done later (hopefully), among other for avoiding dependency to AntLR, delegate geometry parsing to JTS/ESRI library and for trying to avoid direct dependency to JTS. In the meantime, the module is available on geoapi=4.0 branch for experiment but will be excluded from Apache SIS 1.0 release.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 25a4935bf5fbfbc024b2f1c0a46bdb1a3e879197
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Aug 26 16:51:55 2019 +0200

    Minor reformating and test refactoring. Some module of CQL reviews will be done later (hopefully),
    among other for avoiding dependency to AntLR, delegate geometry parsing to JTS/ESRI library and for
    trying to avoid direct dependency to JTS. In the meantime, the module is available on geoapi=4.0 branch
    for experiment but will be excluded from Apache SIS 1.0 release.
---
 core/sis-cql/nb-configuration.xml                  | 18 ----
 core/sis-cql/pom.xml                               | 40 +--------
 .../src/main/java/org/apache/sis/cql/CQL.java      | 28 ++-----
 .../main/java/org/apache/sis/cql/CQLException.java | 20 ++++-
 .../org/apache/sis/cql/FilterToCQLVisitor.java     | 13 ++-
 .../java/org/apache/sis/internal/cql/AntlrCQL.java | 22 +++--
 .../{TemporalUtilities.java => CQLTestCase.java}   | 32 ++++++-
 .../test/java/org/apache/sis/cql/CQLTestSuite.java | 11 ++-
 .../org/apache/sis/cql/ExpressionReadingTest.java  | 97 ++++++++++------------
 .../org/apache/sis/cql/ExpressionWritingTest.java  | 65 +++++++--------
 .../java/org/apache/sis/cql/FilterReadingTest.java | 45 +++++-----
 .../java/org/apache/sis/cql/FilterWritingTest.java | 45 +++++-----
 12 files changed, 199 insertions(+), 237 deletions(-)

diff --git a/core/sis-cql/nb-configuration.xml b/core/sis-cql/nb-configuration.xml
deleted file mode 100644
index 8b36389..0000000
--- a/core/sis-cql/nb-configuration.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project-shared-configuration>
-    <!--
-This file contains additional configuration written by modules in the NetBeans IDE.
-The configuration is intended to be shared among all the users of project and
-therefore it is assumed to be part of version control checkout.
-Without this configuration present, some functionality in the IDE may be limited or fail altogether.
--->
-    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
-        <!--
-Properties that influence various parts of the IDE, especially code formatting and the like. 
-You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
-That way multiple projects can share the same settings (useful for formatting rules for example).
-Any value defined here will override the pom.xml file value but is only applicable to the current project.
--->
-        <netbeans.hint.jdkPlatform>JDK_10</netbeans.hint.jdkPlatform>
-    </properties>
-</project-shared-configuration>
diff --git a/core/sis-cql/pom.xml b/core/sis-cql/pom.xml
index feec340..5aa10c4 100644
--- a/core/sis-cql/pom.xml
+++ b/core/sis-cql/pom.xml
@@ -48,17 +48,8 @@
        =========================================================== -->
   <developers>
     <developer>
-      <name>Travis L. Pinney</name>
-      <id>tlpinney</id>
-      <email>travis.pinney@gmail.com</email>
-      <roles>
-        <role>developer</role>
-      </roles>
-    </developer>
-    <developer>
-      <name>Martin Desruisseaux</name>
-      <id>desruisseaux</id>
-      <email>desruisseaux@apache.org</email>
+      <name>Johann Sorel</name>
+      <id>jsorel</id>
       <organization>Geomatys</organization>
       <organizationUrl>http://www.geomatys.com</organizationUrl>
       <timezone>+1</timezone>
@@ -67,14 +58,6 @@
       </roles>
     </developer>
   </developers>
-  <contributors>
-    <contributor>
-      <name>Marc le Bihan</name>
-      <roles>
-        <role>developer</role>
-      </roles>
-    </contributor>
-  </contributors>
 
 
   <!-- ===========================================================
@@ -138,11 +121,6 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>com.esri.geometry</groupId>
-      <artifactId>esri-geometry-api</artifactId>
-      <optional>true</optional>
-    </dependency>
-    <dependency>
       <groupId>org.locationtech.jts</groupId>
       <artifactId>jts-core</artifactId>
       <optional>true</optional>
@@ -162,20 +140,6 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.sis.core</groupId>
-      <artifactId>sis-metadata</artifactId>
-      <version>${project.version}</version>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.sis.core</groupId>
-      <artifactId>sis-referencing</artifactId>
-      <version>${project.version}</version>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
 </project>
diff --git a/core/sis-cql/src/main/java/org/apache/sis/cql/CQL.java b/core/sis-cql/src/main/java/org/apache/sis/cql/CQL.java
index 8adfe41..2c543db 100644
--- a/core/sis-cql/src/main/java/org/apache/sis/cql/CQL.java
+++ b/core/sis-cql/src/main/java/org/apache/sis/cql/CQL.java
@@ -23,7 +23,6 @@ import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
 import org.apache.sis.filter.DefaultFilterFactory;
 import org.apache.sis.internal.cql.AntlrCQL;
-import static org.apache.sis.internal.cql.CQLParser.*;
 import org.apache.sis.internal.cql.CQLParser.CoordinateContext;
 import org.apache.sis.internal.cql.CQLParser.CoordinateSerieContext;
 import org.apache.sis.internal.cql.CQLParser.CoordinateSeriesContext;
@@ -51,11 +50,14 @@ import org.opengis.filter.Or;
 import org.opengis.filter.expression.Expression;
 import org.opengis.filter.expression.PropertyName;
 
+import static org.apache.sis.internal.cql.CQLParser.*;
+
+
 /**
  *
- * @author Johann Sorel (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
- * @since 1.0
+ * @since   1.0
  * @module
  */
 public final class CQL {
@@ -71,7 +73,6 @@ public final class CQL {
 
     public static Expression parseExpression(String cql, FilterFactory2 factory) throws CQLException {
         final Object obj = AntlrCQL.compileExpression(cql);
-
         ParseTree tree = null;
         Expression result = null;
         if (obj instanceof ExpressionContext) {
@@ -81,7 +82,6 @@ public final class CQL {
             }
             result = convertExpression(tree, factory);
         }
-
         return result;
     }
 
@@ -92,7 +92,7 @@ public final class CQL {
     public static Filter parseFilter(String cql, FilterFactory2 factory) throws CQLException {
         cql = cql.trim();
 
-        //bypass parsing for inclusive filter
+        // Bypass parsing for inclusive filter.
         if (cql.isEmpty() || "*".equals(cql)) {
             return Filter.INCLUDE;
         }
@@ -108,7 +108,6 @@ public final class CQL {
             }
             result = convertFilter(tree, factory);
         }
-
         return result;
     }
 
@@ -116,7 +115,6 @@ public final class CQL {
         if (filter == null) {
             return "";
         }
-
         final StringBuilder sb = new StringBuilder();
         filter.accept(FilterToCQLVisitor.INSTANCE, sb);
         return sb.toString();
@@ -126,7 +124,6 @@ public final class CQL {
         if (exp == null) {
             return "";
         }
-
         final StringBuilder sb = new StringBuilder();
         exp.accept(FilterToCQLVisitor.INSTANCE, sb);
         return sb.toString();
@@ -136,7 +133,6 @@ public final class CQL {
      * Convert the given tree in an Expression.
      */
     private static Expression convertExpression(ParseTree tree, FilterFactory2 ff) throws CQLException {
-
         if (tree instanceof ExpressionContext) {
             //: expression MULT expression
             //| expression UNARY expression
@@ -199,7 +195,7 @@ public final class CQL {
                     return ff.property(name);
                 }
 
-                //handle as a function
+                // Handle as a function.
                 final List<ExpressionContext> params = prm.expression();
                 final List<Expression> exps = new ArrayList<Expression>();
                 for (int i = 0, n = params.size(); i < n; i++) {
@@ -207,7 +203,6 @@ public final class CQL {
                 }
                 return ff.function(name, exps.toArray(new Expression[exps.size()]));
             }
-
         } else if (tree instanceof ExpressionUnaryContext) {
             //: UNARY? expressionNum ;
             final ExpressionUnaryContext exp = (ExpressionUnaryContext) tree;
@@ -379,10 +374,8 @@ public final class CQL {
                 }
                 return ff.literal(geom);
             }
-
             return convertExpression(tree.getChild(0), ff);
         }
-
         throw new CQLException("Unreconized expression : type=" + tree.getText());
     }
 
@@ -422,7 +415,6 @@ public final class CQL {
      * Convert the given tree in a Filter.
      */
     private static Filter convertFilter(ParseTree tree, FilterFactory2 ff) throws CQLException {
-
         if (tree instanceof FilterContext) {
             //: filter (AND filter)+
             //| filter (OR filter)+
@@ -672,7 +664,7 @@ public final class CQL {
                 return ff.toverlaps(left, right);
 
             } else if (exp.filterGeometry() != null) {
-                //expression filterGeometry
+                // expression filterGeometry
                 return convertFilter(exp.filterGeometry(), ff);
             }
 
@@ -754,11 +746,7 @@ public final class CQL {
                 final Expression exp2 = convertExpression(exps.get(1), ff);
                 return ff.within(exp1, exp2);
             }
-
         }
-
         throw new CQLException("Unreconized filter : type=" + tree.getText());
-
     }
-
 }
diff --git a/core/sis-cql/src/main/java/org/apache/sis/cql/CQLException.java b/core/sis-cql/src/main/java/org/apache/sis/cql/CQLException.java
index ab49755..88fc422 100644
--- a/core/sis-cql/src/main/java/org/apache/sis/cql/CQLException.java
+++ b/core/sis-cql/src/main/java/org/apache/sis/cql/CQLException.java
@@ -18,21 +18,35 @@ package org.apache.sis.cql;
 
 
 /**
- * CQL exception.
+ * Thrown when a CQL statement can not be parsed.
  *
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
-public final class CQLException extends Exception{
+public final class CQLException extends Exception {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = -1494977914551289727L;
 
+    /**
+     * Constructs an exception with the specified detail message.
+     *
+     * @param  message  the detail message.
+     */
     public CQLException(String message) {
         super(message);
     }
 
+    /**
+     * Constructs an exception with the specified detail message and cause.
+     *
+     * @param  message  the detail message.
+     * @param  cause    the cause for this exception.
+     */
     public CQLException(String message, Throwable cause) {
         super(message, cause);
     }
-
 }
diff --git a/core/sis-cql/src/main/java/org/apache/sis/cql/FilterToCQLVisitor.java b/core/sis-cql/src/main/java/org/apache/sis/cql/FilterToCQLVisitor.java
index f1ca38b..dd72df6 100644
--- a/core/sis-cql/src/main/java/org/apache/sis/cql/FilterToCQLVisitor.java
+++ b/core/sis-cql/src/main/java/org/apache/sis/cql/FilterToCQLVisitor.java
@@ -18,8 +18,6 @@ package org.apache.sis.cql;
 
 import java.util.Date;
 import java.util.List;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
 import java.util.regex.Pattern;
 import org.apache.sis.internal.util.StandardDateFormat;
 import org.locationtech.jts.geom.Geometry;
@@ -78,19 +76,19 @@ import org.opengis.filter.temporal.TContains;
 import org.opengis.filter.temporal.TEquals;
 import org.opengis.filter.temporal.TOverlaps;
 
+
 /**
- * Visitor to convert a Filter in CQL.<br>
+ * Visitor to convert a Filter in CQL.
  * Returned object is a StringBuilder containing the CQL text.
  *
- * @author Johann Sorel (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
- * @since 1.0
+ * @since   1.0
  * @module
  */
 final class FilterToCQLVisitor implements FilterVisitor, ExpressionVisitor {
 
-    public static final FilterToCQLVisitor INSTANCE = new FilterToCQLVisitor();
-    private static final TimeZone TZ = new SimpleTimeZone(0, "Out Timezone");
+    static final FilterToCQLVisitor INSTANCE = new FilterToCQLVisitor();
 
     /**
      * Pattern to check for property name to escape against regExp
@@ -655,5 +653,4 @@ final class FilterToCQLVisitor implements FilterVisitor, ExpressionVisitor {
     public Object visit(final NilExpression exp, final Object o) {
         throw new UnsupportedOperationException("NilExpression not supported in CQL.");
     }
-
 }
diff --git a/core/sis-cql/src/main/java/org/apache/sis/internal/cql/AntlrCQL.java b/core/sis-cql/src/main/java/org/apache/sis/internal/cql/AntlrCQL.java
index f63ddc8..1f3649a 100644
--- a/core/sis-cql/src/main/java/org/apache/sis/internal/cql/AntlrCQL.java
+++ b/core/sis-cql/src/main/java/org/apache/sis/internal/cql/AntlrCQL.java
@@ -23,12 +23,13 @@ import org.antlr.v4.runtime.RecognitionException;
 import org.antlr.v4.runtime.TokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
 
+
 /**
  * ANTLR CQL parser methods.
- * 
- * @author Johann Sorel (Geomatys)
+ *
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
- * @since 1.0
+ * @since   1.0
  * @module
  */
 public final class AntlrCQL {
@@ -38,22 +39,20 @@ public final class AntlrCQL {
 
     public static ParseTree compile(String cql) {
         final Object obj = compileFilterOrExpression(cql);
-
         ParseTree tree = null;
         if (obj instanceof ParseTree) {
             tree = (ParseTree) obj;
         }
-
         return tree;
     }
 
     public static Object compileExpression(String cql) {
         try {
-            //lexer splits input into tokens
+            // Lexer splits input into tokens.
             final CodePointCharStream input = CharStreams.fromString(cql);
             final TokenStream tokens = new CommonTokenStream(new CQLLexer(input));
 
-            //parser generates abstract syntax tree
+            // Parser generates abstract syntax tree.
             final CQLParser parser = new CQLParser(tokens);
             final CQLParser.ExpressionContext ctx = parser.expression();
             return ctx;
@@ -65,11 +64,11 @@ public final class AntlrCQL {
 
     public static Object compileFilter(String cql) {
         try {
-            //lexer splits input into tokens
+            // Lexer splits input into tokens.
             final CodePointCharStream input = CharStreams.fromString(cql);
             final TokenStream tokens = new CommonTokenStream(new CQLLexer(input));
 
-            //parser generates abstract syntax tree
+            // Parser generates abstract syntax tree.
             final CQLParser parser = new CQLParser(tokens);
             final CQLParser.FilterContext retfilter = parser.filter();
 
@@ -82,11 +81,11 @@ public final class AntlrCQL {
 
     public static Object compileFilterOrExpression(String cql) {
         try {
-            //lexer splits input into tokens
+            // Lexer splits input into tokens.
             final CodePointCharStream input = CharStreams.fromString(cql);
             final TokenStream tokens = new CommonTokenStream(new CQLLexer(input));
 
-            //parser generates abstract syntax tree
+            // Parser generates abstract syntax tree.
             final CQLParser parser = new CQLParser(tokens);
             final CQLParser.FilterOrExpressionContext retfilter = parser.filterOrExpression();
 
@@ -96,5 +95,4 @@ public final class AntlrCQL {
             throw new IllegalStateException("Recognition exception is never thrown, only declared.");
         }
     }
-
 }
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/TemporalUtilities.java b/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestCase.java
similarity index 53%
rename from core/sis-cql/src/test/java/org/apache/sis/cql/TemporalUtilities.java
rename to core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestCase.java
index 158ed14..a6c9c26 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/TemporalUtilities.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestCase.java
@@ -16,19 +16,45 @@
  */
 package org.apache.sis.cql;
 
+import java.time.Instant;
 import java.util.Date;
+import org.opengis.filter.FilterFactory2;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.apache.sis.filter.DefaultFilterFactory;
+import org.apache.sis.test.TestCase;
+
 
 /**
+ * Base class of all CQL tests.
  *
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
-class TemporalUtilities {
+strictfp class CQLTestCase extends TestCase {
+    /**
+     * The factory to use for creating filter and expressions.
+     */
+    final FilterFactory2 FF;
+
+    /**
+     * The factory to use for creating Java Topology Suite (JTS) objects.
+     */
+    final GeometryFactory GF;
 
-    static Date parseDate(String date) {
-        throw new UnsupportedOperationException("todo");
+    /**
+     * Creates a new test case.
+     */
+    CQLTestCase() {
+        FF = new DefaultFilterFactory();
+        GF = new GeometryFactory();
     }
 
+    /**
+     * Returns a date from the given string.
+     */
+    static Date parseDate(final String date) {
+        return Date.from(Instant.parse(date));
+    }
 }
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestSuite.java b/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestSuite.java
index 6bf9783..aa8762b 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestSuite.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/CQLTestSuite.java
@@ -17,6 +17,7 @@
 package org.apache.sis.cql;
 
 import org.apache.sis.test.TestSuite;
+import org.junit.BeforeClass;
 import org.junit.runners.Suite;
 
 
@@ -35,5 +36,13 @@ import org.junit.runners.Suite;
     org.apache.sis.cql.FilterWritingTest.class,
 })
 public final strictfp class CQLTestSuite extends TestSuite {
-
+    /**
+     * Verifies the list of tests before to run the suite.
+     * See {@link #verifyTestList(Class, Class[])} for more information.
+     */
+    @BeforeClass
+    public static void verifyTestList() {
+        assertNoMissingTest(CQLTestSuite.class);
+        verifyTestList(CQLTestSuite.class);
+    }
 }
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionReadingTest.java b/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionReadingTest.java
index 68b7cc2..795b34e 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionReadingTest.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionReadingTest.java
@@ -17,14 +17,9 @@
 package org.apache.sis.cql;
 
 import java.text.ParseException;
-import org.apache.sis.filter.DefaultFilterFactory;
-import static org.junit.Assert.*;
-import org.junit.Ignore;
-import org.junit.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryCollection;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LineString;
 import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.MultiLineString;
@@ -32,7 +27,6 @@ import org.locationtech.jts.geom.MultiPoint;
 import org.locationtech.jts.geom.MultiPolygon;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
-import org.opengis.filter.FilterFactory2;
 import org.opengis.filter.expression.Add;
 import org.opengis.filter.expression.Divide;
 import org.opengis.filter.expression.Function;
@@ -40,6 +34,11 @@ import org.opengis.filter.expression.Literal;
 import org.opengis.filter.expression.Multiply;
 import org.opengis.filter.expression.PropertyName;
 import org.opengis.filter.expression.Subtract;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
 
 /**
  * Test reading CQL expressions.
@@ -49,13 +48,9 @@ import org.opengis.filter.expression.Subtract;
  * @since   1.0
  * @module
  */
-public class ExpressionReadingTest {
-
-    private final FilterFactory2 FF = new DefaultFilterFactory();
-    private final GeometryFactory GF = new GeometryFactory();
-
+public final strictfp class ExpressionReadingTest extends CQLTestCase {
     @Test
-    public void testPropertyName1() throws CQLException{
+    public void testPropertyName1() throws CQLException {
         final String cql = "geom";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof PropertyName);
@@ -64,7 +59,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testPropertyName2() throws CQLException{
+    public void testPropertyName2() throws CQLException {
         final String cql = "\"geom\"";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof PropertyName);
@@ -73,7 +68,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testPropertyName3() throws CQLException{
+    public void testPropertyName3() throws CQLException {
         final String cql = "ùth{e_$uglY^_pr@perté";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof PropertyName);
@@ -82,7 +77,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testInteger() throws CQLException{
+    public void testInteger() throws CQLException {
         final String cql = "15";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -91,7 +86,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testNegativeInteger() throws CQLException{
+    public void testNegativeInteger() throws CQLException {
         final String cql = "-15";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -100,7 +95,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testDecimal1() throws CQLException{
+    public void testDecimal1() throws CQLException {
         final String cql = "3.14";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -109,7 +104,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testDecimal2() throws CQLException{
+    public void testDecimal2() throws CQLException {
         final String cql = "9e-1";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -118,7 +113,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testNegativeDecimal() throws CQLException{
+    public void testNegativeDecimal() throws CQLException {
         final String cql = "-3.14";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -127,7 +122,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testText() throws CQLException{
+    public void testText() throws CQLException {
         final String cql = "'hello world'";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -136,7 +131,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testText2() throws CQLException{
+    public void testText2() throws CQLException {
         final String cql = "'Valle d\\'Aosta'";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -145,7 +140,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testText3() throws CQLException{
+    public void testText3() throws CQLException {
         final String cql = "'Valle d\\'Aosta/Vallée d\\'Aoste'";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -161,7 +156,7 @@ public class ExpressionReadingTest {
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
         final Literal expression = (Literal) obj;
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), expression.getValue());
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), expression.getValue());
     }
 
     @Ignore
@@ -189,7 +184,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testAddition() throws CQLException{
+    public void testAddition() throws CQLException {
         final String cql = "3 + 2";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Add);
@@ -199,7 +194,7 @@ public class ExpressionReadingTest {
 
     @Ignore
     @Test
-    public void testAddition2() throws CQLException{
+    public void testAddition2() throws CQLException {
         final String cql = "'test' + '23'";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Add);
@@ -210,7 +205,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testSubstract() throws CQLException{
+    public void testSubstract() throws CQLException {
         final String cql = "3 - 2";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Subtract);
@@ -219,7 +214,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testMultiply() throws CQLException{
+    public void testMultiply() throws CQLException {
         final String cql = "3 * 2";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Multiply);
@@ -228,7 +223,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testDivide() throws CQLException{
+    public void testDivide() throws CQLException {
         final String cql = "3 / 2";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Divide);
@@ -238,7 +233,7 @@ public class ExpressionReadingTest {
 
     @Ignore
     @Test
-    public void testFunction1() throws CQLException{
+    public void testFunction1() throws CQLException {
         final String cql = "max(\"att\",15)";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Function);
@@ -248,7 +243,7 @@ public class ExpressionReadingTest {
 
     @Ignore
     @Test
-    public void testFunction2() throws CQLException{
+    public void testFunction2() throws CQLException {
         final String cql = "min(\"att\",cos(3.14))";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Function);
@@ -257,7 +252,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryPoint() throws CQLException{
+    public void testGeometryPoint() throws CQLException {
         final String cql = "POINT(15 30)";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -267,7 +262,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryPointEmpty() throws CQLException{
+    public void testGeometryPointEmpty() throws CQLException {
         final String cql = "POINT EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -278,7 +273,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMPoint() throws CQLException{
+    public void testGeometryMPoint() throws CQLException {
         final String cql = "MULTIPOINT(15 30, 45 60)";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -292,7 +287,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMPointEmpty() throws CQLException{
+    public void testGeometryMPointEmpty() throws CQLException {
         final String cql = "MULTIPOINT EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -303,7 +298,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryLineString() throws CQLException{
+    public void testGeometryLineString() throws CQLException {
         final String cql = "LINESTRING(10 20, 30 40, 50 60)";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -318,7 +313,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryLineStringEmpty() throws CQLException{
+    public void testGeometryLineStringEmpty() throws CQLException {
         final String cql = "LINESTRING EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -329,7 +324,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMLineString() throws CQLException{
+    public void testGeometryMLineString() throws CQLException {
         final String cql = "MULTILINESTRING((10 20, 30 40, 50 60),(70 80, 90 100, 110 120))";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -354,7 +349,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMLineStringEmpty() throws CQLException{
+    public void testGeometryMLineStringEmpty() throws CQLException {
         final String cql = "MULTILINESTRING EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -365,7 +360,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryPolygon() throws CQLException{
+    public void testGeometryPolygon() throws CQLException {
         final String cql = "POLYGON((10 20, 30 40, 50 60, 10 20), (70 80, 90 100, 110 120, 70 80))";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -392,7 +387,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryPolygonEmpty() throws CQLException{
+    public void testGeometryPolygonEmpty() throws CQLException {
         final String cql = "POLYGON EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -403,7 +398,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMPolygon() throws CQLException{
+    public void testGeometryMPolygon() throws CQLException {
         final String cql = "MULTIPOLYGON("
                 + "((10 20, 30 40, 50 60, 10 20), (70 80, 90 100, 110 120, 70 80)),"
                 + "((11 21, 31 41, 51 61, 11 21), (71 81, 91 101, 111 121, 71 81))"
@@ -452,7 +447,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryMPolygonEmpty() throws CQLException{
+    public void testGeometryMPolygonEmpty() throws CQLException {
         final String cql = "MULTIPOLYGON EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -463,7 +458,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryCollection() throws CQLException{
+    public void testGeometryCollection() throws CQLException {
         final String cql = "GEOMETRYCOLLECTION( POINT(15 30), LINESTRING(10 20, 30 40, 50 60) )";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -483,7 +478,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryCollectionEmpty() throws CQLException{
+    public void testGeometryCollectionEmpty() throws CQLException {
         final String cql = "GEOMETRYCOLLECTION EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -494,7 +489,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryEnvelope() throws CQLException{
+    public void testGeometryEnvelope() throws CQLException {
         final String cql = "ENVELOPE(10, 20, 40, 30)";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -514,7 +509,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testGeometryEnvelopeEmpty() throws CQLException{
+    public void testGeometryEnvelopeEmpty() throws CQLException {
         final String cql = "ENVELOPE EMPTY";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Literal);
@@ -525,7 +520,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testCombine1() throws CQLException{
+    public void testCombine1() throws CQLException {
         final String cql = "((3*1)+(2-6))/4";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Divide);
@@ -541,7 +536,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testCombine2() throws CQLException{
+    public void testCombine2() throws CQLException {
         final String cql = "3*1+2/4";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Add);
@@ -558,7 +553,7 @@ public class ExpressionReadingTest {
 
     @Ignore
     @Test
-    public void testCombine3() throws CQLException{
+    public void testCombine3() throws CQLException {
         final String cql = "3*max(val,15)+2/4";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Add);
@@ -578,7 +573,7 @@ public class ExpressionReadingTest {
 
     @Ignore
     @Test
-    public void testCombine4() throws CQLException{
+    public void testCombine4() throws CQLException {
         final String cql = "3 * max ( val , 15 ) + 2 / 4";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Add);
@@ -597,7 +592,7 @@ public class ExpressionReadingTest {
     }
 
     @Test
-    public void testCombine5() throws CQLException{
+    public void testCombine5() throws CQLException {
         final String cql = "(\"NB-Curistes\"*50)/12000";
         final Object obj = CQL.parseExpression(cql);
         assertTrue(obj instanceof Divide);
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionWritingTest.java b/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionWritingTest.java
index 708064f..f48fa76 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionWritingTest.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/ExpressionWritingTest.java
@@ -17,18 +17,17 @@
 package org.apache.sis.cql;
 
 import java.text.ParseException;
-import org.apache.sis.filter.DefaultFilterFactory;
-import static org.junit.Assert.*;
-import org.junit.Ignore;
-import org.junit.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LineString;
 import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.Polygon;
-import org.opengis.filter.FilterFactory2;
 import org.opengis.filter.expression.Expression;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
 
 /**
  * Test writing in CQL expressions.
@@ -38,13 +37,9 @@ import org.opengis.filter.expression.Expression;
  * @since   1.0
  * @module
  */
-public class ExpressionWritingTest {
-
-    private final FilterFactory2 FF = new DefaultFilterFactory();
-    private final GeometryFactory GF = new GeometryFactory();
-
+public final strictfp class ExpressionWritingTest extends CQLTestCase {
     @Test
-    public void testPropertyName1() throws CQLException{
+    public void testPropertyName1() throws CQLException {
         final Expression exp = FF.property("geom");
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -52,7 +47,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testPropertyName2() throws CQLException{
+    public void testPropertyName2() throws CQLException {
         final Expression exp = FF.property("the geom");
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -60,7 +55,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testInteger() throws CQLException{
+    public void testInteger() throws CQLException {
         final Expression exp = FF.literal(15);
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -68,7 +63,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testNegativeInteger() throws CQLException{
+    public void testNegativeInteger() throws CQLException {
         final Expression exp = FF.literal(-15);
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -76,7 +71,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testDecimal1() throws CQLException{
+    public void testDecimal1() throws CQLException {
         final Expression exp = FF.literal(3.14);
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -84,7 +79,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testDecimal2() throws CQLException{
+    public void testDecimal2() throws CQLException {
         final Expression exp = FF.literal(9.0E-21);
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -94,7 +89,7 @@ public class ExpressionWritingTest {
     @Ignore
     @Test
     public void testDate() throws CQLException, ParseException{
-        final Expression exp = FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"));
+        final Expression exp = FF.literal(parseDate("2012-03-21T05:42:36Z"));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
         assertEquals("2012-03-21T05:42:36Z", cql);
@@ -102,7 +97,7 @@ public class ExpressionWritingTest {
 
 
     @Test
-    public void testNegativeDecimal() throws CQLException{
+    public void testNegativeDecimal() throws CQLException {
         final Expression exp = FF.literal(-3.14);
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -110,7 +105,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testText() throws CQLException{
+    public void testText() throws CQLException {
         final Expression exp = FF.literal("hello world");
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -118,7 +113,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testAdd() throws CQLException{
+    public void testAdd() throws CQLException {
         final Expression exp = FF.add(FF.literal(3),FF.literal(2));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -126,7 +121,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testSubtract() throws CQLException{
+    public void testSubtract() throws CQLException {
         final Expression exp = FF.subtract(FF.literal(3),FF.literal(2));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -134,7 +129,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testMultiply() throws CQLException{
+    public void testMultiply() throws CQLException {
         final Expression exp = FF.multiply(FF.literal(3),FF.literal(2));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -142,7 +137,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testDivide() throws CQLException{
+    public void testDivide() throws CQLException {
         final Expression exp = FF.divide(FF.literal(3),FF.literal(2));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -151,7 +146,7 @@ public class ExpressionWritingTest {
 
     @Ignore
     @Test
-    public void testFunction1() throws CQLException{
+    public void testFunction1() throws CQLException {
         final Expression exp = FF.function("max",FF.property("att"), FF.literal(15));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -160,7 +155,7 @@ public class ExpressionWritingTest {
 
     @Ignore
     @Test
-    public void testFunction2() throws CQLException{
+    public void testFunction2() throws CQLException {
         final Expression exp = FF.function("min",FF.property("att"), FF.function("cos",FF.literal(3.14d)));
         final String cql = CQL.write(exp);
         assertNotNull(cql);
@@ -168,7 +163,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testCombine1() throws CQLException{
+    public void testCombine1() throws CQLException {
         final Expression exp =
                 FF.divide(
                     FF.add(
@@ -182,7 +177,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testCombine2() throws CQLException{
+    public void testCombine2() throws CQLException {
         final Expression exp =
                 FF.add(
                         FF.multiply(FF.literal(3), FF.literal(1)),
@@ -196,7 +191,7 @@ public class ExpressionWritingTest {
 
     @Ignore
     @Test
-    public void testCombine3() throws CQLException{
+    public void testCombine3() throws CQLException {
         final Expression exp =
                 FF.add(
                         FF.multiply(
@@ -211,7 +206,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testPoint() throws CQLException{
+    public void testPoint() throws CQLException {
         final Geometry geom = GF.createPoint(new Coordinate(15, 30));
         final Expression exp = FF.literal(geom);
         final String cql = CQL.write(exp);
@@ -220,7 +215,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testMPoint() throws CQLException{
+    public void testMPoint() throws CQLException {
         final Geometry geom = GF.createMultiPoint(
                 new Coordinate[]{
                     new Coordinate(15, 30),
@@ -233,7 +228,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testLineString() throws CQLException{
+    public void testLineString() throws CQLException {
         final Geometry geom = GF.createLineString(
                 new Coordinate[]{
                     new Coordinate(10, 20),
@@ -247,7 +242,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testMLineString() throws CQLException{
+    public void testMLineString() throws CQLException {
         final Geometry geom = GF.createMultiLineString(
                 new LineString[]{
                     GF.createLineString(
@@ -271,7 +266,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testPolygon() throws CQLException{
+    public void testPolygon() throws CQLException {
         final Geometry geom = GF.createPolygon(
                 GF.createLinearRing(
                     new Coordinate[]{
@@ -297,7 +292,7 @@ public class ExpressionWritingTest {
     }
 
     @Test
-    public void testMPolygon() throws CQLException{
+    public void testMPolygon() throws CQLException {
         final Polygon geom1 = GF.createPolygon(
                 GF.createLinearRing(
                     new Coordinate[]{
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/FilterReadingTest.java b/core/sis-cql/src/test/java/org/apache/sis/cql/FilterReadingTest.java
index e2946a8..ab2f673 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/FilterReadingTest.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/FilterReadingTest.java
@@ -18,18 +18,11 @@ package org.apache.sis.cql;
 
 import java.text.ParseException;
 import java.util.Date;
-import org.apache.sis.filter.DefaultFilterFactory;
-import org.apache.sis.internal.util.UnmodifiableArrayList;
-import static org.junit.Assert.*;
-import org.junit.Ignore;
-import org.junit.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LinearRing;
 import org.opengis.filter.And;
 import org.opengis.filter.Filter;
-import org.opengis.filter.FilterFactory2;
 import org.opengis.filter.Not;
 import org.opengis.filter.Or;
 import org.opengis.filter.PropertyIsBetween;
@@ -68,6 +61,12 @@ import org.opengis.filter.temporal.OverlappedBy;
 import org.opengis.filter.temporal.TContains;
 import org.opengis.filter.temporal.TEquals;
 import org.opengis.filter.temporal.TOverlaps;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
 
 /**
  * Test reading CQL filters.
@@ -77,11 +76,9 @@ import org.opengis.filter.temporal.TOverlaps;
  * @since   1.0
  * @module
  */
-public class FilterReadingTest {
+public final strictfp class FilterReadingTest extends CQLTestCase {
 
     private static final double DELTA = 0.00000001;
-    private final FilterFactory2 FF = new DefaultFilterFactory();
-    private final GeometryFactory GF = new GeometryFactory();
     private final Geometry baseGeometry = GF.createPolygon(
                 GF.createLinearRing(
                     new Coordinate[]{
@@ -648,7 +645,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -663,7 +660,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -678,7 +675,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -693,7 +690,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -708,7 +705,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -723,7 +720,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -738,7 +735,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -753,7 +750,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -768,7 +765,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -783,7 +780,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -798,7 +795,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -813,7 +810,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -828,7 +825,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
     @Ignore
@@ -843,7 +840,7 @@ public class FilterReadingTest {
         assertTrue(filter.getExpression2() instanceof Literal);
         assertTrue( ((Literal)filter.getExpression2()).getValue() instanceof Date);
         final Date filterdate = (Date) ((Literal)filter.getExpression2()).getValue();
-        assertEquals(TemporalUtilities.parseDate("2012-03-21T05:42:36Z"), filterdate);
+        assertEquals(parseDate("2012-03-21T05:42:36Z"), filterdate);
     }
 
 }
diff --git a/core/sis-cql/src/test/java/org/apache/sis/cql/FilterWritingTest.java b/core/sis-cql/src/test/java/org/apache/sis/cql/FilterWritingTest.java
index 26a671a..ff908a8 100644
--- a/core/sis-cql/src/test/java/org/apache/sis/cql/FilterWritingTest.java
+++ b/core/sis-cql/src/test/java/org/apache/sis/cql/FilterWritingTest.java
@@ -18,17 +18,16 @@ package org.apache.sis.cql;
 
 import java.text.ParseException;
 import java.util.Collections;
-import org.apache.sis.filter.DefaultFilterFactory;
-import org.apache.sis.internal.util.UnmodifiableArrayList;
-import static org.junit.Assert.*;
-import org.junit.Ignore;
-import org.junit.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LinearRing;
 import org.opengis.filter.Filter;
-import org.opengis.filter.FilterFactory2;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
 
 /**
  * Test writing in CQL filters.
@@ -38,10 +37,8 @@ import org.opengis.filter.FilterFactory2;
  * @since   1.0
  * @module
  */
-public class FilterWritingTest {
+public final strictfp class FilterWritingTest extends CQLTestCase {
 
-    private final FilterFactory2 FF = new DefaultFilterFactory();
-    private final GeometryFactory GF = new GeometryFactory();
     private final Geometry baseGeometry = GF.createPolygon(
                 GF.createLinearRing(
                     new Coordinate[]{
@@ -290,7 +287,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testAfter() throws CQLException, ParseException {
-        final Filter filter = FF.after(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.after(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att AFTER 2012-03-21T05:42:36Z", cql);
@@ -299,7 +296,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testAnyInteracts() throws CQLException, ParseException {
-        final Filter filter = FF.anyInteracts(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.anyInteracts(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att ANYINTERACTS 2012-03-21T05:42:36Z", cql);
@@ -308,7 +305,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testBefore() throws CQLException, ParseException {
-        final Filter filter = FF.before(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.before(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att BEFORE 2012-03-21T05:42:36Z", cql);
@@ -317,7 +314,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testBegins() throws CQLException, ParseException {
-        final Filter filter = FF.begins(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.begins(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att BEGINS 2012-03-21T05:42:36Z", cql);
@@ -326,7 +323,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testBegunBy() throws CQLException, ParseException {
-        final Filter filter = FF.begunBy(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.begunBy(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att BEGUNBY 2012-03-21T05:42:36Z", cql);
@@ -335,7 +332,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testDuring() throws CQLException, ParseException {
-        final Filter filter = FF.during(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.during(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att DURING 2012-03-21T05:42:36Z", cql);
@@ -344,7 +341,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testEndedBy() throws CQLException, ParseException {
-        final Filter filter = FF.endedBy(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.endedBy(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att ENDEDBY 2012-03-21T05:42:36Z", cql);
@@ -353,7 +350,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testEnds() throws CQLException, ParseException {
-        final Filter filter = FF.ends(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.ends(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att ENDS 2012-03-21T05:42:36Z", cql);
@@ -362,7 +359,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testMeets() throws CQLException, ParseException {
-        final Filter filter = FF.meets(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.meets(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att MEETS 2012-03-21T05:42:36Z", cql);
@@ -371,7 +368,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testMetBy() throws CQLException, ParseException {
-        final Filter filter = FF.metBy(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.metBy(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att METBY 2012-03-21T05:42:36Z", cql);
@@ -380,7 +377,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testOverlappedBy() throws CQLException, ParseException {
-        final Filter filter = FF.overlappedBy(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.overlappedBy(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att OVERLAPPEDBY 2012-03-21T05:42:36Z", cql);
@@ -389,7 +386,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testTcontains() throws CQLException, ParseException {
-        final Filter filter = FF.tcontains(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.tcontains(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att TCONTAINS 2012-03-21T05:42:36Z", cql);
@@ -398,7 +395,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testTequals() throws CQLException, ParseException {
-        final Filter filter = FF.tequals(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.tequals(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att TEQUALS 2012-03-21T05:42:36Z", cql);
@@ -407,7 +404,7 @@ public class FilterWritingTest {
     @Ignore
     @Test
     public void testToverlaps() throws CQLException, ParseException {
-        final Filter filter = FF.toverlaps(FF.property("att"), FF.literal(TemporalUtilities.parseDate("2012-03-21T05:42:36Z")));
+        final Filter filter = FF.toverlaps(FF.property("att"), FF.literal(parseDate("2012-03-21T05:42:36Z")));
         final String cql = CQL.write(filter);
         assertNotNull(cql);
         assertEquals("att TOVERLAPS 2012-03-21T05:42:36Z", cql);


[sis] 01/03: Verify also the ComparisonFunction name during tests, and verify at least one of the two expression operands. Recycle some previously created objects, and add comments about what is being tested in TemporalFunctionTest.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 5961fbcdefbcceea661fc73f470d4b7da17e753e
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Aug 26 15:47:21 2019 +0200

    Verify also the ComparisonFunction name during tests, and verify at least one of the two expression operands.
    Recycle some previously created objects, and add comments about what is being tested in TemporalFunctionTest.
---
 .../apache/sis/filter/ComparisonFunctionTest.java  | 101 +++++++++++++++------
 .../apache/sis/filter/TemporalFunctionTest.java    |  58 +++++++++++-
 2 files changed, 128 insertions(+), 31 deletions(-)

diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/ComparisonFunctionTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/ComparisonFunctionTest.java
index 183ae06..cfe4dff 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/ComparisonFunctionTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/ComparisonFunctionTest.java
@@ -16,11 +16,13 @@
  */
 package org.apache.sis.filter;
 
-import static org.apache.sis.test.Assert.*;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.expression.Literal;
+import org.opengis.filter.BinaryComparisonOperator;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
-import org.opengis.filter.Filter;
-import org.opengis.filter.FilterFactory2;
+
+import static org.apache.sis.test.Assert.*;
 
 
 /**
@@ -35,16 +37,56 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
     /**
      * The factory to use for creating the objects to test.
      */
-    private final FilterFactory2 factory = new DefaultFilterFactory();
+    private final FilterFactory factory;
+
+    /**
+     * Expressions used as constant for the tests.
+     */
+    private final Literal c05, c10, c20;
+
+    /**
+     * Expected name of the filter to be evaluated. The {@link #evaluate(BinaryComparisonOperator)} method
+     * will compare {@link ComparisonFunction#getName()} against this value.
+     */
+    private String expectedName;
+
+    /**
+     * The filter tested by last call to {@link #evaluate(BinaryComparisonOperator)}.
+     */
+    private BinaryComparisonOperator filter;
+
+    /**
+     * Creates a new test case.
+     */
+    public ComparisonFunctionTest() {
+        factory = new DefaultFilterFactory();
+        c05 = factory.literal(5);
+        c10 = factory.literal(10);
+        c20 = factory.literal(20);
+    }
+
+    /**
+     * Evaluates the given filter. The {@link #expectedName} field must be set before this method is invoked.
+     * This method assumes that the first expression of all filters is {@link #c10}.
+     */
+    private boolean evaluate(final BinaryComparisonOperator filter) {
+        this.filter = filter;
+        assertInstanceOf("Expected SIS implementation.", ComparisonFunction.class, filter);
+        assertEquals("name", expectedName, ((ComparisonFunction) filter).getName());
+        assertSame("expression1", c10, filter.getExpression1());
+        return filter.evaluate(null);
+    }
 
     /**
      * Tests "LessThan" (construction, evaluation, serialization, equality).
      */
     @Test
     public void testLess() {
-        assertFilter(true,  factory.less(factory.literal(10), factory.literal(20)));
-        assertFilter(false, factory.less(factory.literal(10), factory.literal(10)));
-        assertFilter(false, factory.less(factory.literal(10), factory.literal(5)));
+        expectedName = "LessThan";
+        assertTrue (evaluate(factory.less(c10, c20)));
+        assertFalse(evaluate(factory.less(c10, c10)));
+        assertFalse(evaluate(factory.less(c10, c05)));
+        assertSerializedEquals(filter);
     }
 
     /**
@@ -52,9 +94,11 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
      */
     @Test
     public void testLessOrEqual() {
-        assertFilter(true,  factory.lessOrEqual(factory.literal(10), factory.literal(20)));
-        assertFilter(true,  factory.lessOrEqual(factory.literal(10), factory.literal(10)));
-        assertFilter(false, factory.lessOrEqual(factory.literal(10), factory.literal(5)));
+        expectedName = "LessThanOrEqualTo";
+        assertTrue (evaluate(factory.lessOrEqual(c10, c20)));
+        assertTrue (evaluate(factory.lessOrEqual(c10, c10)));
+        assertFalse(evaluate(factory.lessOrEqual(c10, c05)));
+        assertSerializedEquals(filter);
     }
 
     /**
@@ -62,9 +106,11 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
      */
     @Test
     public void testGreater() {
-        assertFilter(false, factory.greater(factory.literal(10), factory.literal(20)));
-        assertFilter(false, factory.greater(factory.literal(10), factory.literal(10)));
-        assertFilter(true,  factory.greater(factory.literal(10), factory.literal(5)));
+        expectedName = "GreaterThan";
+        assertFalse(evaluate(factory.greater(c10, c20)));
+        assertFalse(evaluate(factory.greater(c10, c10)));
+        assertTrue (evaluate(factory.greater(c10, c05)));
+        assertSerializedEquals(filter);
     }
 
     /**
@@ -72,9 +118,11 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
      */
     @Test
     public void testGreaterOrEqual() {
-        assertFilter(false, factory.greaterOrEqual(factory.literal(10), factory.literal(20)));
-        assertFilter(true,  factory.greaterOrEqual(factory.literal(10), factory.literal(10)));
-        assertFilter(true,  factory.greaterOrEqual(factory.literal(10), factory.literal(5)));
+        expectedName = "GreaterThanOrEqualTo";
+        assertFalse(evaluate(factory.greaterOrEqual(c10, c20)));
+        assertTrue (evaluate(factory.greaterOrEqual(c10, c10)));
+        assertTrue (evaluate(factory.greaterOrEqual(c10, c05)));
+        assertSerializedEquals(filter);
     }
 
     /**
@@ -82,9 +130,11 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
      */
     @Test
     public void testEqual() {
-        assertFilter(false, factory.equals(factory.literal(10), factory.literal(20)));
-        assertFilter(true,  factory.equals(factory.literal(10), factory.literal(10)));
-        assertFilter(false, factory.equals(factory.literal(10), factory.literal(5)));
+        expectedName = "EqualTo";
+        assertFalse(evaluate(factory.equals(c10, c20)));
+        assertTrue (evaluate(factory.equals(c10, c10)));
+        assertFalse(evaluate(factory.equals(c10, c05)));
+        assertSerializedEquals(filter);
     }
 
     /**
@@ -92,13 +142,10 @@ public final strictfp class ComparisonFunctionTest extends TestCase {
      */
     @Test
     public void testNotEqual() {
-        assertFilter(true,  factory.notEqual(factory.literal(10), factory.literal(20)));
-        assertFilter(false, factory.notEqual(factory.literal(10), factory.literal(10)));
-        assertFilter(true,  factory.notEqual(factory.literal(10), factory.literal(5)));
-    }
-
-    private static void assertFilter(boolean expected, Filter op) {
-        assertEquals(expected, op.evaluate(null));
-        assertSerializedEquals(op);
+        expectedName = "NotEqualTo";
+        assertTrue (evaluate(factory.notEqual(c10, c20)));
+        assertFalse(evaluate(factory.notEqual(c10, c10)));
+        assertTrue (evaluate(factory.notEqual(c10, c05)));
+        assertSerializedEquals(filter);
     }
 }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/TemporalFunctionTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/TemporalFunctionTest.java
index c46c512..507d33d 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/TemporalFunctionTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/TemporalFunctionTest.java
@@ -66,15 +66,15 @@ public final strictfp class TemporalFunctionTest extends TestCase {
 
     /**
      * Performs some validation on newly created filter.
+     * The {@link #filter} field must be initialized before this method is invoked.
      *
      * @param  name  expected filter name.
      */
     private void validate(final String name) {
         assertInstanceOf("Expected SIS implementation.", TemporalFunction.class, filter);
-        final TemporalFunction f = ((TemporalFunction) filter);
-        assertEquals("name", name, f.getName());
-        assertSame("expression1", expression1, f.expression1);
-        assertSame("expression2", expression2, f.expression2);
+        assertEquals("name", name, ((TemporalFunction) filter).getName());
+        assertSame("expression1", expression1, filter.getExpression1());
+        assertSame("expression2", expression2, filter.getExpression2());
         assertSerializedEquals(filter);
     }
 
@@ -93,6 +93,8 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.tequals(expression1, expression2);
         validate("TEquals");
         assertTrue(evaluate());
+
+        // Break the "self.end = other.end" condition.
         expression1.end++;
         assertFalse(evaluate());
     }
@@ -105,9 +107,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.before(expression1, expression2);
         validate("Before");
         assertFalse(evaluate());
+
+        // Move before expression 2.
         expression1.begin -= 10 * MILLISECONDS_PER_DAY;
         expression1.end   -= 10 * MILLISECONDS_PER_DAY;
         assertTrue(evaluate());
+
+        // Break the "self.end < other.begin" condition.
         expression1.end = expression2.begin;
         assertFalse(evaluate());
     }
@@ -120,9 +126,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.after(expression1, expression2);
         validate("After");
         assertFalse(evaluate());
+
+        // Move after expression 2.
         expression1.begin += 10 * MILLISECONDS_PER_DAY;
         expression1.end   += 10 * MILLISECONDS_PER_DAY;
         assertTrue(evaluate());
+
+        // Break the "self.begin > other.end" condition.
         expression1.begin = expression2.end;
         assertFalse(evaluate());
     }
@@ -135,8 +145,12 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.begins(expression1, expression2);
         validate("Begins");
         assertFalse(evaluate());
+
+        // End before ending of expression 2.
         expression1.end--;
         assertTrue(evaluate());
+
+        // Break the "self.begin = other.begin" condition.
         expression1.begin++;
         assertFalse(evaluate());
     }
@@ -149,8 +163,12 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.ends(expression1, expression2);
         validate("Ends");
         assertFalse(evaluate());
+
+        // Begin after beginning of expression 2.
         expression1.begin++;
         assertTrue(evaluate());
+
+        // Break the "self.end = other.end" condition.
         expression1.end--;
         assertFalse(evaluate());
     }
@@ -163,8 +181,12 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.begunBy(expression1, expression2);
         validate("BegunBy");
         assertFalse(evaluate());
+
+        // End after ending of expression 2.
         expression1.end++;
         assertTrue(evaluate());
+
+        // Break the "self.begin = other.begin" condition.
         expression1.begin--;
         assertFalse(evaluate());
     }
@@ -177,8 +199,12 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.endedBy(expression1, expression2);
         validate("EndedBy");
         assertFalse(evaluate());
+
+        // Begin before beginning of expression 2.
         expression1.begin--;
         assertTrue(evaluate());
+
+        // Break the "self.end = other.end" condition.
         expression1.end++;
         assertFalse(evaluate());
     }
@@ -191,9 +217,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.meets(expression1, expression2);
         validate("Meets");
         assertFalse(evaluate());
+
+        // Move before expression 2.
         expression1.begin -= 10 * MILLISECONDS_PER_DAY;
         expression1.end   -= 10 * MILLISECONDS_PER_DAY;
         assertFalse(evaluate());
+
+        // Met the "self.end = other.begin" condition.
         expression1.end = expression2.begin;
         assertTrue(evaluate());
     }
@@ -206,9 +236,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.metBy(expression1, expression2);
         validate("MetBy");
         assertFalse(evaluate());
+
+        // Move after expression 2.
         expression1.begin += 10 * MILLISECONDS_PER_DAY;
         expression1.end   += 10 * MILLISECONDS_PER_DAY;
         assertFalse(evaluate());
+
+        // Met the "self.begin = other.end" condition.
         expression1.begin = expression2.end;
         assertTrue(evaluate());
     }
@@ -221,9 +255,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.during(expression1, expression2);
         validate("During");
         assertFalse(evaluate());
+
+        // Shrink inside expression 2.
         expression1.begin++;
         expression1.end--;
         assertTrue(evaluate());
+
+        // Break the "self.end < other.end" condition.
         expression1.end += 2;
         assertFalse(evaluate());
     }
@@ -236,9 +274,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.tcontains(expression1, expression2);
         validate("TContains");
         assertFalse(evaluate());
+
+        // Expand to encompass expression 2.
         expression1.begin--;
         expression1.end++;
         assertTrue(evaluate());
+
+        // Break the "self.end > other.end" condition.
         expression1.end -= 2;
         assertFalse(evaluate());
     }
@@ -251,9 +293,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.toverlaps(expression1, expression2);
         validate("TOverlaps");
         assertFalse(evaluate());
+
+        // Translate to overlap left part of expression 2.
         expression1.begin--;
         expression1.end--;
         assertTrue(evaluate());
+
+        // Break the "self.end < other.end" condition.
         expression1.end += 2;
         assertFalse(evaluate());
     }
@@ -266,9 +312,13 @@ public final strictfp class TemporalFunctionTest extends TestCase {
         filter = factory.overlappedBy(expression1, expression2);
         validate("OverlappedBy");
         assertFalse(evaluate());
+
+        // Translate to overlap right part of expression 2.
         expression1.begin++;
         expression1.end++;
         assertTrue(evaluate());
+
+        // Break the "self.end > other.end" condition.
         expression1.end -= 2;
         assertFalse(evaluate());
     }


[sis] 03/03: Hide SQLMM class from public API (the function that it provides are available through DefaultFilterFactory) and refactor tests.

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a206c7bd933939d81820e89703ec65590c5334ff
Author: Martin Desruisseaux <ma...@geomatys.com>
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);
     }
 }