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 <[email protected]>
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());
+ }
}