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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new d60db2c5d3 Make filter functions available in `FilterCapabilities`.
d60db2c5d3 is described below

commit d60db2c5d3c8aba6f6c151885286f3f4bf6a1436
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Aug 3 19:54:36 2023 +0200

    Make filter functions available in `FilterCapabilities`.
---
 .../java/org/apache/sis/filter/Capabilities.java   |  25 +++-
 .../apache/sis/filter/DefaultFilterFactory.java    | 130 ++++++++++++++++++---
 .../apache/sis/internal/feature/Geometries.java    |   2 +-
 .../sis/internal/filter/FunctionRegister.java      |   4 +-
 .../internal/filter/sqlmm/FunctionDescription.java |  96 ++++++++++++++-
 .../apache/sis/internal/filter/sqlmm/SQLMM.java    |   2 +-
 .../org/apache/sis/filter/CapabilitiesTest.java    |  22 +++-
 7 files changed, 245 insertions(+), 36 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/filter/Capabilities.java 
b/core/sis-feature/src/main/java/org/apache/sis/filter/Capabilities.java
index 81910b8c35..74b5ec99b4 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/Capabilities.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/Capabilities.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.filter;
 
+import java.util.Map;
 import java.util.Set;
 import java.util.Collection;
 import java.util.Optional;
@@ -27,6 +28,7 @@ import org.apache.sis.internal.feature.AttributeConvention;
 import org.opengis.filter.ComparisonOperatorName;
 import org.opengis.filter.capability.Conformance;
 import org.opengis.filter.capability.IdCapabilities;
+import org.opengis.filter.capability.AvailableFunction;
 import org.opengis.filter.capability.FilterCapabilities;
 import org.opengis.filter.capability.ScalarCapabilities;
 import org.opengis.filter.capability.SpatialCapabilities;
@@ -40,19 +42,24 @@ import org.opengis.filter.capability.TemporalCapabilities;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 final class Capabilities implements FilterCapabilities, Conformance, 
IdCapabilities, ScalarCapabilities {
     /**
-     * The unique instance of the capabilities document.
+     * The filter factory which is providing the functions.
+     *
+     * @see #getFunctions()
      */
-    static final Capabilities INSTANCE = new Capabilities();
+    private final DefaultFilterFactory<?,?,?> factory;
 
     /**
      * Creates a new capability document.
+     *
+     * @param  factory  the filter factory which is providing functions.
      */
-    private Capabilities() {
+    Capabilities(final DefaultFilterFactory<?,?,?> factory) {
+        this.factory = factory;
     }
 
     /**
@@ -149,4 +156,14 @@ final class Capabilities implements FilterCapabilities, 
Conformance, IdCapabilit
     public boolean implementsSorting() {
         return true;
     }
+
+    /**
+     * Enumerates the functions that may be used in filter expressions.
+     *
+     * @return the function that may be used in filter expressions.
+     */
+    @Override
+    public Map<String,AvailableFunction> getFunctions() {
+        return factory.new Functions();
+    }
 }
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 536c1fa8d2..cefc741868 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,6 +18,7 @@ package org.apache.sis.filter;
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Collection;
 import java.util.ServiceLoader;
 import java.time.Instant;
@@ -34,10 +35,12 @@ import org.apache.sis.geometry.WraparoundMethod;
 import org.apache.sis.util.iso.AbstractFactory;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.AbstractMap;
 
 // Branch-dependent imports
 import org.opengis.filter.*;
 import org.opengis.feature.Feature;
+import org.opengis.filter.capability.AvailableFunction;
 import org.opengis.filter.capability.FilterCapabilities;
 
 
@@ -127,7 +130,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends 
AbstractFactory implem
      *
      * @return factory operating on {@link Feature} instances.
      *
-     * @todo The type of temporal object is not yet determined.
+     * @todo The type of temporal objects is not yet determined.
      */
     public static FilterFactory<Feature, Object, Object> forFeatures() {
         return Features.DEFAULT;
@@ -142,7 +145,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends 
AbstractFactory implem
      */
     @Override
     public FilterCapabilities getCapabilities() {
-        return Capabilities.INSTANCE;
+        return new Capabilities(this);              // Cheap to construct, no 
need to cache.
     }
 
     /**
@@ -160,7 +163,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends 
AbstractFactory implem
          * @see #forFeatures()
          */
         static final FilterFactory<Feature,Object,Object> DEFAULT =
-                new Features<>(Object.class, Object.class, 
WraparoundMethod.SPLIT);;
+                new Features<>(Object.class, Object.class, 
WraparoundMethod.SPLIT);
 
         /**
          * Creates a new factory operating on {@link Feature} instances.
@@ -992,23 +995,14 @@ public abstract class DefaultFilterFactory<R,G,T> extends 
AbstractFactory implem
     }
 
     /**
-     * Creates an implementation-specific function.
-     * The names of available functions is given by {@link #getCapabilities()}.
+     * Returns the provider for the function of the given name.
+     * If the given name is {@code null}, then this method only
+     * ensures that {@link #availableFunctions} is initialized.
      *
-     * @param  name        name of the function to call.
-     * @param  parameters  expressions providing values for the function 
arguments.
-     * @return an expression which will call the specified function.
-     * @throws IllegalArgumentException if the given name is not recognized,
-     *         or if the arguments are illegal for the specified function.
+     * @param  name  name of the function to get, or {@code null} if none.
+     * @return the register for the given function, or {@code null} if none.
      */
-    @Override
-    public Expression<R,?> function(final String name, Expression<R,?>[] 
parameters) {
-        ArgumentChecks.ensureNonNull("name", name);
-        ArgumentChecks.ensureNonNull("parameters", parameters);
-        parameters = parameters.clone();
-        for (int i=0; i<parameters.length; i++) {
-            ArgumentChecks.ensureNonNullElement("parameters", i, 
parameters[i]);
-        }
+    private FunctionRegister register(final String name) {
         final FunctionRegister register;
         synchronized (availableFunctions) {
             if (availableFunctions.isEmpty()) {
@@ -1030,6 +1024,28 @@ public abstract class DefaultFilterFactory<R,G,T> 
extends AbstractFactory implem
             }
             register = availableFunctions.get(name);
         }
+        return register;
+    }
+
+    /**
+     * Creates an implementation-specific function.
+     * The names of available functions is given by {@link #getCapabilities()}.
+     *
+     * @param  name        name of the function to call.
+     * @param  parameters  expressions providing values for the function 
arguments.
+     * @return an expression which will call the specified function.
+     * @throws IllegalArgumentException if the given name is not recognized,
+     *         or if the arguments are illegal for the specified function.
+     */
+    @Override
+    public Expression<R,?> function(final String name, Expression<R,?>[] 
parameters) {
+        ArgumentChecks.ensureNonNull("name", name);
+        ArgumentChecks.ensureNonNull("parameters", parameters);
+        parameters = parameters.clone();
+        for (int i=0; i<parameters.length; i++) {
+            ArgumentChecks.ensureNonNullElement("parameters", i, 
parameters[i]);
+        }
+        final FunctionRegister register = register(name);
         if (register == null) {
             throw new 
IllegalArgumentException(Resources.format(Resources.Keys.UnknownFunction_1, 
name));
         }
@@ -1037,7 +1053,83 @@ public abstract class DefaultFilterFactory<R,G,T> 
extends AbstractFactory implem
     }
 
     /**
-     * Indicates an property by which contents should be sorted, along with 
intended order.
+     * Map of all functions supported by this factory, together with their 
providers.
+     * This is a view over {@link #availableFunctions} which delegates 
descriptions
+     * to {@link FunctionRegister#describe(String)}. No values is stored in 
this map.
+     *
+     * @see Capabilities#getFunctions()
+     */
+    final class Functions extends AbstractMap<String, AvailableFunction> {
+        /**
+         * Creates a new map.
+         */
+        Functions() {
+        }
+
+        /**
+         * {@return the number of functions}.
+         */
+        @Override
+        public int size() {
+            synchronized (availableFunctions) {
+                register(null);     // Ensure that `availableFunctions` is 
initialized.
+                return availableFunctions.size();
+            }
+        }
+
+        /**
+         * Returns the description of the function of the given name.
+         * This method delegates to {@link FunctionRegister#describe(String)}.
+         *
+         * @param  key  name of the function to describe.
+         * @return description of the requested function, or {@code null} if 
none.
+         */
+        @Override
+        public AvailableFunction get(final Object key) {
+            if (key instanceof String) {
+                final String name = (String) key;
+                final FunctionRegister register = register(name);
+                if (register != null) {
+                    return register.describe(name);
+                }
+            }
+            return null;
+        }
+
+        /**
+         * {@return an iterator over the entries in this map}.
+         */
+        @Override
+        protected EntryIterator<String, AvailableFunction> entryIterator() {
+            final Iterator<Entry<String, FunctionRegister>> it;
+            synchronized (availableFunctions) {
+                register(null);     // Ensure that `availableFunctions` is 
initialized.
+                it = availableFunctions.entrySet().iterator();
+            }
+            /*
+             * Following is theoretically not thread-safe, but it is okay in 
our case
+             * because the `availableFunctions` map is not changed after 
construction.
+             */
+            return new EntryIterator<>() {
+                private Entry<String, FunctionRegister> entry;
+
+                @Override protected boolean next() {
+                    return (entry = it.hasNext() ? it.next() : null) != null;
+                }
+
+                @Override protected String getKey() {
+                    return entry.getKey();
+                }
+
+                @Override protected AvailableFunction getValue() {
+                    return entry.getValue().describe(getKey());
+                }
+            };
+        }
+    }
+
+    /**
+     * Indicates a property by which contents should be sorted, along with 
intended order.
      * The given expression should evaluate to {@link Comparable} objects,
      * but {@link Iterable} objects are accepted as well.
      *
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
index 564d4fa847..08e7953081 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
@@ -143,7 +143,7 @@ public abstract class Geometries<G> implements Serializable 
{
      *
      * @param  library  the desired library, or {@code null} for the default.
      * @return the specified or the default geometry implementation (never 
{@code null}).
-     * @throws IllegalArgumentException if a non-null library is specified by 
that library is not available.
+     * @throws IllegalArgumentException if a non-null library is specified but 
that library is not available.
      */
     public static Geometries<?> factory(final GeometryLibrary library) {
         Geometries<?> g = GeometryFactories.DEFAULT;
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FunctionRegister.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FunctionRegister.java
index 0a147fcae7..15c6344218 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FunctionRegister.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FunctionRegister.java
@@ -37,8 +37,6 @@ import org.opengis.filter.capability.AvailableFunction;
  * @since   1.0
  *
  * @see org.opengis.filter.FilterFactory#function(String, Expression...)
- *
- * @todo Replace by {@link org.opengis.filter.capability.ExtendedCapabilities}.
  */
 public interface FunctionRegister {
     /**
@@ -60,7 +58,7 @@ public interface FunctionRegister {
     /**
      * Describes the parameters of a function.
      *
-     * @param  name        name of the function to describe (not null).
+     * @param  name  name of the function to describe (not null).
      * @return description of the function parameters.
      * @throws IllegalArgumentException if function name is unknown..
      */
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionDescription.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionDescription.java
index d3b8407ef9..137c19ac84 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionDescription.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/FunctionDescription.java
@@ -32,7 +32,7 @@ import org.opengis.filter.capability.AvailableFunction;
  * Description of a SQLMM function with its parameters.
  *
  * @todo Argument descriptions are incomplete. They have no good names,
- *       and the types are missing (null) except for geometry types.
+ *       and the types are missing (they are {@code null}) except for geometry 
types.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.4
@@ -120,9 +120,7 @@ final class FunctionDescription implements 
AvailableFunction {
     }
 
     /**
-     * Returns the type of return value.
-     *
-     * @return the type of return value.
+     * {@return the type of return value}.
      */
     @Override
     public TypeName getReturnType() {
@@ -172,5 +170,95 @@ final class FunctionDescription implements 
AvailableFunction {
         public TypeName getValueType() {
             return type;
         }
+
+        /**
+         * Tests whether the given object is equal to this argument 
description.
+         *
+         * @param  obj  the object to test for equality.
+         * @return whether the given object describes the same argument than 
this.
+         */
+        @Override
+        public boolean equals(final Object obj) {
+            if (obj instanceof Arg) {
+                final var other = (Arg) obj;
+                return name.equals(other.name)
+                    && type.equals(other.type);
+            }
+            return false;
+        }
+
+        /**
+         * {@return a hash-code value for this argument description}.
+         */
+        @Override
+        public int hashCode() {
+            return name.hashCode() + type.hashCode();
+        }
+
+        /**
+         * {@return a string representation of this argument}.
+         * Current version includes the name and the type.
+         * Should be used only for debugging purposes.
+         */
+        @Override
+        public String toString() {
+            final var sb = new StringBuilder(20);
+            addType(sb.append(name), type);
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Appends the given type name if non-null.
+     *
+     * @param sb    where to append the type name.
+     * @param type  the type name to add, or {@code null} if none.
+     */
+    private static void addType(final StringBuilder sb, final TypeName type) {
+        if (type != null) {
+            sb.append(" : ").append(type);
+        }
+    }
+
+    /**
+     * Tests whether the given object is equal to this function description.
+     *
+     * @param  obj  the object to test for equality.
+     * @return whether the given object describes the same function than this.
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj instanceof FunctionDescription) {
+            final var other = (FunctionDescription) obj;
+            return name.equals(other.name)
+                && result.equals(other.result)
+                && arguments.equals(other.arguments);
+        }
+        return false;
+    }
+
+    /**
+     * {@return a hash-code value for this function description}.
+     */
+    @Override
+    public int hashCode() {
+        return name.hashCode() + arguments.hashCode() + result.hashCode();
+    }
+
+    /**
+     * {@return a string representation of this function with its argument}.
+     * Should be used only for debugging purposes.
+     */
+    @Override
+    public String toString() {
+        final var sb = new StringBuilder(40).append(name).append('(');
+        boolean isMore = false;
+        for (final Argument arg : getArguments()) {
+            if (isMore) sb.append(", ");
+            addType(sb.append(arg.getName()), arg.getValueType());
+            isMore = true;
+        }
+        addType(sb.append(')'), getReturnType());
+        return sb.toString();
     }
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
index f43e86f6e8..bc796d1350 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
@@ -842,7 +842,7 @@ public enum SQLMM {
      * Returns a description of this SQLMM function.
      * The Java types associated to arguments and return value depend on which 
geometry library is used.
      *
-     * @param  library  the geometry ilibrary implementation to use.
+     * @param  library  the geometry library implementation to use.
      * @return description of this SQLMM function.
      */
     public final synchronized AvailableFunction description(final 
Geometries<?> library) {
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/filter/CapabilitiesTest.java 
b/core/sis-feature/src/test/java/org/apache/sis/filter/CapabilitiesTest.java
index 77b9e699d3..9d92781b14 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/filter/CapabilitiesTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/CapabilitiesTest.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.*;
 import org.opengis.filter.ComparisonOperatorName;
 import org.opengis.filter.capability.IdCapabilities;
 import org.opengis.filter.capability.ScalarCapabilities;
+import org.opengis.filter.capability.AvailableFunction;
 
 
 /**
@@ -35,7 +36,7 @@ import org.opengis.filter.capability.ScalarCapabilities;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 public final class CapabilitiesTest extends TestCase {
@@ -50,8 +51,9 @@ public final class CapabilitiesTest extends TestCase {
      */
     @Test
     public void testResourceIdentifiers() {
-        
assertTrue(Capabilities.INSTANCE.getConformance().implementsResourceld());
-        final IdCapabilities idc = 
Capabilities.INSTANCE.getIdCapabilities().get();
+        final var capabilities = 
DefaultFilterFactory.forFeatures().getCapabilities();
+        assertTrue(capabilities.getConformance().implementsResourceld());
+        final IdCapabilities idc = capabilities.getIdCapabilities().get();
         final LocalName id = 
TestUtilities.getSingleton(idc.getResourceIdentifiers());
         assertEquals("identifier", id.toString());
     }
@@ -61,10 +63,22 @@ public final class CapabilitiesTest extends TestCase {
      */
     @Test
     public void testComparisonOperators() {
-        final ScalarCapabilities c = 
Capabilities.INSTANCE.getScalarCapabilities().get();
+        final var capabilities = 
DefaultFilterFactory.forFeatures().getCapabilities();
+        final ScalarCapabilities c = 
capabilities.getScalarCapabilities().get();
         final Set<ComparisonOperatorName> op = c.getComparisonOperators();
         assertTrue(op.contains(ComparisonOperatorName.PROPERTY_IS_EQUAL_TO));
         assertTrue(op.contains(ComparisonOperatorName.PROPERTY_IS_LESS_THAN));
         
assertTrue(op.contains(ComparisonOperatorName.PROPERTY_IS_GREATER_THAN));
     }
+
+    /**
+     * Tests {@link Capabilities#getFunctions()}.
+     */
+    @Test
+    public void testFunctions() {
+        final var capabilities = 
DefaultFilterFactory.forFeatures().getCapabilities();
+        AvailableFunction desc = 
capabilities.getFunctions().get("ST_Transform");
+        assertEquals("SQLMM:ST_Transform", 
desc.getName().toFullyQualifiedName().toString());
+        assertEquals("OGC:Geometry", 
desc.getReturnType().toFullyQualifiedName().toString());
+    }
 }

Reply via email to