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 90823cb52c Refactor `Capabilities` for deferred loading of
`FunctionRegistry` and for case-insensitive search of function names.
90823cb52c is described below
commit 90823cb52c7bb3d1728cc6715b3f2f68529c6c83
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Oct 31 17:04:47 2025 +0100
Refactor `Capabilities` for deferred loading of `FunctionRegistry` and for
case-insensitive search of function names.
---
.../main/org/apache/sis/filter/Capabilities.java | 280 ++++++++++++++++++++-
.../apache/sis/filter/DefaultFilterFactory.java | 133 +---------
.../org/apache/sis/filter/FunctionRegister.java | 16 +-
.../main/org/apache/sis/filter/math/Function.java | 12 +-
.../main/org/apache/sis/filter/math/Registry.java | 22 +-
.../main/org/apache/sis/filter/sqlmm/Registry.java | 20 +-
.../apache/sis/filter/visitor/FunctionNames.java | 14 ++
.../storage/sql/feature/SelectionClauseWriter.java | 4 +-
.../sis/util/internal/shared/AbstractIterator.java | 2 +-
.../sis/util/internal/shared/CollectionsExt.java | 22 ++
10 files changed, 346 insertions(+), 179 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
index 240218641c..fa59d42a3e 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Capabilities.java
@@ -19,8 +19,23 @@ package org.apache.sis.filter;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ServiceLoader;
import java.util.Optional;
import org.opengis.util.LocalName;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.collection.CodeListSet;
+import org.apache.sis.util.internal.shared.AbstractMap;
+import org.apache.sis.referencing.internal.shared.LazySet;
+import org.apache.sis.feature.internal.shared.AttributeConvention;
+import org.apache.sis.geometry.wrapper.Geometries;
+import org.apache.sis.filter.sqlmm.Registry;
+import org.apache.sis.system.Reflect;
+
+// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.filter.Expression;
import org.opengis.filter.ComparisonOperatorName;
import org.opengis.filter.capability.Conformance;
import org.opengis.filter.capability.IdCapabilities;
@@ -29,33 +44,78 @@ import org.opengis.filter.capability.FilterCapabilities;
import org.opengis.filter.capability.ScalarCapabilities;
import org.opengis.filter.capability.SpatialCapabilities;
import org.opengis.filter.capability.TemporalCapabilities;
-import org.apache.sis.util.collection.CodeListSet;
-import org.apache.sis.feature.internal.shared.AttributeConvention;
/**
- * Metadata about the specific elements that Apache SIS implementation
supports.
+ * Metadata about the specific elements that Apache <abbr>SIS</abbr>
implementation supports.
+ * This is also an unmodifiable map of all functions supported by this factory,
+ * together with their providers. This class is thread-safe.
*
* @todo Missing {@link SpatialCapabilities} and {@link TemporalCapabilities}.
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
*/
-final class Capabilities implements FilterCapabilities, Conformance,
IdCapabilities, ScalarCapabilities {
+@SuppressWarnings("ReturnOfCollectionOrArrayField")
+final class Capabilities extends AbstractMap<String, AvailableFunction>
+ implements FilterCapabilities, Conformance, IdCapabilities,
ScalarCapabilities
+{
+ /**
+ * The providers of functions. Each {@code FunctionRegister} instance
typically provides many functions.
+ * There is one provider for SQL/MM, one provider for mathematical
functions, <i>etc</i>.
+ */
+ private final LazySet<FunctionRegister> providers;
+
+ /**
+ * Providers of functions identified by their names.
+ * Created when first needed and reset to {@code null} when not needed
anymore.
+ *
+ * @see #nextFunctionRegister()
+ */
+ private Iterator<FunctionRegister> registerIterator;
+
+ /**
+ * Names of all functions sorted in increasing order ignoring case.
+ * This array is initially empty and is increased when new providers are
tested.
+ * The array may contain duplicated values if two or more {@link
FunctionRegister}
+ * declare a function of the same name.
+ *
+ * @see #indexOfFunction(String)
+ */
+ private String[] functionNames;
+
/**
- * The filter factory which is providing the functions.
+ * The provider for each function name. The length of this array shall be
equal to the length
+ * of the {@link #functionNames} array. The provider at index <var>i</var>
shall accept a function of
+ * the name at index <var>i</var>.
*
- * @see #getFunctions()
+ * <h4>Design note</h4>
+ * The combination of {@link #functionNames} and {@code providersForName}
could be replaced by {@link java.util.TreeSet}.
+ * But we use array as an efficient way to get both the key (function
name) and value for a given key in order to
+ * resolve the real names in case-insensitive searches. We also need to
support duplicated names.
+ *
+ * @see #createFunction(String, Expression[])
*/
- private final DefaultFilterFactory<?,?,?> factory;
+ private FunctionRegister[] registerForName;
/**
- * Creates a new capability document.
+ * Creates a new capability document and map for the specified geometry
library.
*
- * @param factory the filter factory which is providing functions.
+ * @param geometries the library to use for creating geometry objects.
*/
- Capabilities(final DefaultFilterFactory<?,?,?> factory) {
- this.factory = factory;
+ Capabilities(final Geometries<?> geometries) {
+ functionNames = CharSequences.EMPTY_ARRAY;
+ providers = new LazySet<FunctionRegister>() {
+ /** Returns the SQLMM functions to check first. */
+ @Override protected FunctionRegister[] initialValues() {
+ return new FunctionRegister[] {new Registry(geometries)};
+ }
+
+ /** Returns the other functions (math, etc.) to check after SQLMM.
*/
+ @Override protected Iterator<FunctionRegister>
createSourceIterator() {
+ return ServiceLoader.load(FunctionRegister.class,
Reflect.getContextClassLoader()).iterator();
+ }
+ };
}
/**
@@ -159,7 +219,201 @@ final class Capabilities implements FilterCapabilities,
Conformance, IdCapabilit
* @return the function that may be used in filter expressions.
*/
@Override
- public Map<String,AvailableFunction> getFunctions() {
- return factory.new Functions();
+ public Map<String, AvailableFunction> getFunctions() {
+ return this;
+ }
+
+ /**
+ * Returns the next function register, or {@code null} if none.
+ * This method shall be invoked in a block synchronized on {@code this}.
+ * This method is invoked only for deferred loading of function registers.
+ * After all function registers have been loaded, this method always
returns {@code null}.
+ */
+ private FunctionRegister nextFunctionRegister() {
+ assert Thread.holdsLock(this);
+ if (registerIterator == null) {
+ if (registerForName != null) {
+ return null; // We already extracted all providers.
+ }
+ registerIterator = providers.iterator();
+ }
+ if (!registerIterator.hasNext()) {
+ return null;
+ }
+ final FunctionRegister register = registerIterator.next();
+ /*
+ * Combine the list of functions of the new register with the list of
functions of all previous registers.
+ * If the same name is used twice, the previous registers have
precedence in `registerIterator` order.
+ */
+ final String[] combined = ArraysExt.concatenate(functionNames,
register.getNames().toArray(String[]::new));
+ Arrays.sort(combined, String.CASE_INSENSITIVE_ORDER);
+ final var registers = new FunctionRegister[combined.length];
+ Arrays.fill(registers, register);
+ if (registerForName != null) {
+ // ArrayIndexOutOfBoundsException on `i` should never happen.
+ for (int i=0, s=0; s < functionNames.length; i++) {
+ if (combined[i].equalsIgnoreCase(functionNames[s])) {
+ registers[i] = registerForName[s++];
+ }
+ }
+ }
+ registerForName = registers;
+ functionNames = combined;
+ return register;
+ }
+
+ /**
+ * Returns the index of the provider for the function of the given name.
+ * This method shall be invoked in a block synchronized on {@code this}.
+ * If many providers define a function of the given name, then there is
+ * no guaranteed about which provider is returned.
+ *
+ * @param name case-insensitive name of the function to get.
+ * @return index of the provider for the given name, or -1 if not found.
+ */
+ private int indexOfFunction(final String name) {
+ int i;
+ while ((i = Arrays.binarySearch(functionNames, name,
String.CASE_INSENSITIVE_ORDER)) < 0) {
+ if (nextFunctionRegister() == null) {
+ return -1;
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Always returns {@code false} since we should always have at least the
SQLMM function register.
+ */
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * Returns the number of functions.
+ */
+ @Override
+ @SuppressWarnings("empty-statement")
+ public synchronized int size() {
+ while (nextFunctionRegister() != null); // Ensure that all
providers have been loaded.
+ return functionNames.length;
+ }
+
+ /**
+ * Returns whether this map contains a function of the given name,
ignoring case.
+ *
+ * @param key name of the function to search.
+ * @return whether this map contains a function of the given name,
ignoring case.
+ */
+ @Override
+ public boolean containsKey(final Object key) {
+ if (key instanceof String) {
+ synchronized (this) {
+ return indexOfFunction((String) key) >= 0;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the description of the first function (in iteration order) of
the given name.
+ * This method finds the register, then 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) {
+ String name = (String) key;
+ FunctionRegister register;
+ synchronized (this) {
+ int i = indexOfFunction(name);
+ if (i < 0) return null;
+ while (i != 0 && name.equalsIgnoreCase(functionNames[i-1]))
i--;
+ name = functionNames[i];
+ register = registerForName[i];
+ }
+ return register.describe(name);
+ }
+ return null;
+ }
+
+ /**
+ * Creates an implementation-specific function. If many registers have a
function of the given name,
+ * then this method tries all them in iteration order until one of them
succeed.
+ *
+ * @param <R> type of resources.
+ * @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, or {@code
null} if the given name is unknown.
+ * @throws IllegalArgumentException if the arguments are illegal for the
specified function.
+ */
+ @SuppressWarnings("empty-statement")
+ public <R> Expression<R,?> createFunction(String name, final
Expression<R,?>[] parameters) {
+ final String[] names;
+ final FunctionRegister[] registers;
+ synchronized (this) {
+ int i = indexOfFunction(name);
+ if (i < 0) return null;
+ int upper = i;
+ while (++upper < functionNames.length &&
name.equalsIgnoreCase(functionNames[upper]));
+ while (i != 0 && name.equalsIgnoreCase(functionNames[i-1])) i--;
+ names = Arrays.copyOfRange(functionNames, i, upper);
+ registers = Arrays.copyOfRange(registerForName, i, upper);
+ }
+ IllegalArgumentException cause = null;
+ for (int i=0; i<names.length; i++) try {
+ return registers[i].create(names[i], parameters);
+ } catch (IllegalArgumentException e) {
+ if (cause == null) cause = e;
+ else cause.addSuppressed(e);
+ }
+ if (cause != null) {
+ throw cause;
+ }
+ return null;
+ }
+
+ /**
+ * Returns an iterator over the entries in this map.
+ * This is a lazy iterator which will load providers only if requested.
+ */
+ @Override
+ protected EntryIterator<String, AvailableFunction> entryIterator() {
+ final Iterator<FunctionRegister> it = providers.iterator();
+ return new EntryIterator<>() {
+ /** Iterator over the names of functions of the current provider.
*/
+ private Iterator<String> names;
+
+ /** The next name to return. */
+ private String next;
+
+ /** Returns whether there is a next element. */
+ @Override protected boolean next() {
+ for (;;) {
+ if (names != null && names.hasNext()) {
+ next = names.next();
+ return true;
+ }
+ names = null;
+ if (!it.hasNext()) {
+ next = null;
+ return false;
+ }
+ names = it.next().getNames().iterator();
+ }
+ }
+
+ /** Returns the current function name. */
+ @Override protected String getKey() {
+ return next;
+ }
+
+ /** Returns the current function description. */
+ @Override protected AvailableFunction getValue() {
+ return get(next);
+ }
+ };
}
}
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
index aa7d605961..9afd590393 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
@@ -16,10 +16,7 @@
*/
package org.apache.sis.filter;
-import java.util.Map;
-import java.util.HashMap;
import java.util.Collection;
-import java.util.ServiceLoader;
import java.util.Optional;
import javax.measure.Quantity;
import javax.measure.quantity.Length;
@@ -30,20 +27,17 @@ import org.apache.sis.geometry.WraparoundMethod;
import org.apache.sis.geometry.wrapper.Geometries;
import org.apache.sis.feature.internal.Resources;
import org.apache.sis.filter.base.UnaryFunction;
-import org.apache.sis.filter.sqlmm.Registry;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.iso.AbstractFactory;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.internal.shared.LazyCandidate;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.Iterator;
import java.time.Instant;
import org.opengis.filter.*;
import org.opengis.feature.Feature;
-import org.opengis.filter.capability.AvailableFunction;
import org.opengis.filter.capability.FilterCapabilities;
import org.apache.sis.util.internal.shared.Strings;
-import org.apache.sis.util.internal.shared.AbstractMap;
/**
@@ -53,7 +47,7 @@ import org.apache.sis.util.internal.shared.AbstractMap;
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.5
+ * @version 1.6
*
* @param <R> the type of resources (e.g. {@link
org.opengis.feature.Feature}) to use as inputs.
* @param <G> base class of geometry objects. The implementation-neutral
type is GeoAPI {@link Geometry},
@@ -86,7 +80,8 @@ public abstract class DefaultFilterFactory<R,G,T> extends
AbstractFactory implem
*
* @see #function(String, Expression...)
*/
- private final Map<String,FunctionRegister> availableFunctions;
+ @LazyCandidate
+ private final Capabilities availableFunctions;
/**
* Creates a new factory for geometries and temporal objects of the given
types.
@@ -126,7 +121,7 @@ public abstract class DefaultFilterFactory<R,G,T> extends
AbstractFactory implem
}
this.temporal = temporal;
this.wraparound = wraparound;
- availableFunctions = new HashMap<>();
+ availableFunctions = new Capabilities(library);
}
/**
@@ -172,8 +167,9 @@ public abstract class DefaultFilterFactory<R,G,T> extends
AbstractFactory implem
* @return description of the abilities of this factory.
*/
@Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
public FilterCapabilities getCapabilities() {
- return new Capabilities(this); // Cheap to construct, no
need to cache.
+ return availableFunctions;
}
/**
@@ -1036,37 +1032,6 @@ public abstract class DefaultFilterFactory<R,G,T>
extends AbstractFactory implem
return new ArithmeticFunction.Divide<>(operand1, operand2);
}
- /**
- * 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 get, or {@code null} if none.
- * @return the register for the given function, or {@code null} if none.
- */
- private FunctionRegister register(final String name) {
- final FunctionRegister register;
- synchronized (availableFunctions) {
- if (availableFunctions.isEmpty()) {
- /*
- * Load functions when first needed or if the module path
changed since last invocation.
- * The SQLMM factory is hard-coded because it depends on the
geometry library.
- */
- final Registry r = new Registry(library);
- for (final String fn : r.getNames()) {
- availableFunctions.put(fn, r);
- }
- for (final FunctionRegister er :
ServiceLoader.load(FunctionRegister.class)) {
- for (final String fn : er.getNames()) {
- availableFunctions.putIfAbsent(fn, er);
- }
- }
- }
- register = availableFunctions.get(name);
- }
- return register;
- }
-
/**
* Creates an implementation-specific function.
* The names of available functions is given by {@link #getCapabilities()}.
@@ -1085,87 +1050,11 @@ public abstract class DefaultFilterFactory<R,G,T>
extends AbstractFactory implem
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));
- }
- return register.create(name, parameters);
- }
-
- /**
- * 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() {
- }
-
- /**
- * Returns 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;
- }
-
- /**
- * Returns 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());
- }
- };
+ final Expression<R,?> expression =
availableFunctions.createFunction(name, parameters);
+ if (expression != null) {
+ return expression;
}
+ throw new
IllegalArgumentException(Resources.format(Resources.Keys.UnknownFunction_1,
name));
}
/**
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
index 1789b46fb0..1589a3e661 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
@@ -16,7 +16,7 @@
*/
package org.apache.sis.filter;
-import java.util.Collection;
+import java.util.Set;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.filter.Expression;
@@ -34,7 +34,7 @@ import org.opengis.filter.capability.AvailableFunction;
* Checks against name collisions may be added in a future version.</p>
*
* @author Johann Sorel (Geomatys)
- * @version 1.5
+ * @version 1.6
* @since 1.5
*
* @see org.opengis.filter.FilterFactory#function(String, Expression...)
@@ -49,27 +49,25 @@ public interface FunctionRegister {
/**
* Returns the names of all functions that this factory can create.
- * It is currently implementer responsibility to ensure that there are no
name collisions with
- * functions provided by other factories (this problem may be improved in
future SIS release).
*
* @return set of supported function names.
*/
- Collection<String> getNames();
+ Set<String> getNames();
/**
* Describes the parameters of a function.
*
- * @param name name of the function to describe (not null).
+ * @param name case-sensitive name of the function to describe.
* @return description of the function parameters.
- * @throws IllegalArgumentException if function name is unknown..
+ * @throws IllegalArgumentException if the given function name is unknown.
*/
AvailableFunction describe(String name) throws IllegalArgumentException;
/**
- * Creates a new function of the given name with given parameters.
+ * Creates a new function of the given name with the given parameters.
*
* @param <R> the type of resources (e.g. {@link
org.opengis.feature.Feature}) used as inputs.
- * @param name name of the function to create (not null).
+ * @param name case-sensitive name of the function to create as an
expression.
* @param parameters function parameters.
* @return function for the given name and parameters.
* @throws IllegalArgumentException if function name is unknown or some
parameters are illegal.
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Function.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Function.java
index 344f31d55d..95d2b36864 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Function.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Function.java
@@ -18,7 +18,6 @@ package org.apache.sis.filter.math;
import java.util.Map;
import java.util.List;
-import java.util.Locale;
import java.util.function.DoubleUnaryOperator;
import java.util.function.DoubleBinaryOperator;
import org.opengis.util.TypeName;
@@ -222,24 +221,17 @@ enum Function implements AvailableFunction {
*/
final synchronized ScopedName getFunctionName() {
if (name == null) {
- name = Node.createName(camelCaseName());
+ name = Node.createName(name());
}
return name;
}
- /**
- * Returns the function name in the case to show to users.
- */
- final String camelCaseName() {
- return name().toLowerCase(Locale.US).intern();
- }
-
/**
* Returns the attribute type to declare in feature types that store
result of this function.
*/
final synchronized AttributeType<Double> getResultType() {
if (resultType == null) {
- resultType = Node.createType(Double.class, camelCaseName());
+ resultType = Node.createType(Double.class, name());
}
return resultType;
}
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Registry.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Registry.java
index e38c18a892..d36b1ea8e8 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Registry.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/math/Registry.java
@@ -16,12 +16,10 @@
*/
package org.apache.sis.filter.math;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
+import java.util.Set;
import org.apache.sis.filter.FunctionRegister;
+import org.apache.sis.filter.visitor.FunctionNames;
import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.internal.shared.Constants;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -57,34 +55,34 @@ public final class Registry implements FunctionRegister {
* Returns the names of all functions that this factory can create.
*/
@Override
- public Collection<String> getNames() {
- return Containers.derivedList(Arrays.asList(Function.values()),
Function::camelCaseName);
+ public Set<String> getNames() {
+ return FunctionNames.of(Function.class);
}
/**
* Describes the parameters of a function.
*
- * @param name name of the function to describe (not null).
+ * @param name case-sensitive name of the function to describe.
* @return description of the function parameters.
- * @throws IllegalArgumentException if function name is unknown..
+ * @throws IllegalArgumentException if the given function name is unknown.
*/
@Override
public AvailableFunction describe(String name) {
- return Function.valueOf(name.toUpperCase(Locale.US));
+ return Function.valueOf(name);
}
/**
* Creates a new function of the given name with given parameters.
*
* @param <R> the type of resources (e.g. {@link
org.opengis.feature.Feature}) used as inputs.
- * @param name name of the function to create (not null).
+ * @param name case-sensitive name of the function to create as an
expression.
* @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 <R> Expression<R, ?> create(final String name, final
Expression<R,?>[] parameters) {
- final Function function =
Function.valueOf(name.toUpperCase(Locale.US));
+ public <R> Expression<R,?> create(final String name, final
Expression<R,?>[] parameters) {
+ final Function function = Function.valueOf(name);
ArgumentChecks.ensureCountBetween("parameters", false,
function.getMinParameterCount(),
function.getMaxParameterCount(),
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
index d0a7a26723..545e902304 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
@@ -16,11 +16,10 @@
*/
package org.apache.sis.filter.sqlmm;
-import java.util.Arrays;
-import java.util.Collection;
+import java.util.Set;
import org.apache.sis.geometry.wrapper.Geometries;
import org.apache.sis.filter.FunctionRegister;
-import org.apache.sis.util.collection.Containers;
+import org.apache.sis.filter.visitor.FunctionNames;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.filter.Expression;
@@ -61,16 +60,16 @@ public final class Registry implements FunctionRegister {
* Returns the names of all functions known to this register.
*/
@Override
- public Collection<String> getNames() {
- return Containers.derivedList(Arrays.asList(SQLMM.values()),
SQLMM::name);
+ public Set<String> getNames() {
+ return FunctionNames.of(SQLMM.class);
}
/**
* Describes the parameters of a function.
*
- * @param name name of the function to describe.
+ * @param name case-sensitive name of the function to describe.
* @return description of the function parameters.
- * @throws IllegalArgumentException if function name is unknown..
+ * @throws IllegalArgumentException if the given function name is unknown.
*/
@Override
public AvailableFunction describe(final String name) {
@@ -83,9 +82,10 @@ public final class Registry implements FunctionRegister {
* has been cloned and does not contain null elements.
* This method verifies only the number of parameters.
*
- * @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.
+ * @param <R> the type of resources (e.g. {@link
org.opengis.feature.Feature}) used as inputs.
+ * @param name case-sensitive name of the function to create as an
expression.
+ * @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
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/visitor/FunctionNames.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/visitor/FunctionNames.java
index e1d1b1ad0f..ad4a3bd834 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/visitor/FunctionNames.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/visitor/FunctionNames.java
@@ -16,9 +16,13 @@
*/
package org.apache.sis.filter.visitor;
+import java.util.Arrays;
+import java.util.Set;
import org.opengis.util.CodeList;
import org.apache.sis.filter.DefaultFilterFactory;
import org.apache.sis.filter.sqlmm.SQLMM;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.internal.shared.CollectionsExt;
/**
@@ -96,4 +100,14 @@ public final class FunctionNames {
public static CodeList<?> resourceId() {
return
DefaultFilterFactory.forFeatures().resourceId("resourceId").getOperatorType();
}
+
+ /**
+ * Returns the names of all enumeration values, to be interpreted as
function names.
+ *
+ * @param type type of the enumeration for which to get the names.
+ * @return the names viewed as a collection.
+ */
+ public static Set<String> of(final Class<? extends Enum<?>> type) {
+ return
CollectionsExt.viewAsSet(Containers.derivedList(Arrays.asList(type.getEnumConstants()),
Enum::name));
+ }
}
diff --git
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java
index f60aa46b36..b108cbe6c2 100644
---
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java
+++
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java
@@ -215,7 +215,7 @@ public class SelectionClauseWriter extends Visitor<Feature,
SelectionClause> {
/**
* Invoked when an unsupported filter is found. The SQL string is marked
as invalid and
- * may be truncated (later) to the length that it has the last time that
it was valid.
+ * may be truncated (later) to the length that it had the last time that
it was valid.
*/
@Override
protected final void typeNotFound(CodeList<?> type, Filter<Feature>
filter, SelectionClause sql) {
@@ -224,7 +224,7 @@ public class SelectionClauseWriter extends Visitor<Feature,
SelectionClause> {
/**
* Invoked when an unsupported expression is found. The SQL string is
marked as invalid
- * and may be truncated (later) to the length that it has the last time
that it was valid.
+ * and may be truncated (later) to the length that it had the last time
that it was valid.
*/
@Override
protected final void typeNotFound(String type, Expression<Feature,?>
expression, SelectionClause sql) {
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractIterator.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractIterator.java
index 47c7ff97d5..d0299d2bb1 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractIterator.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/AbstractIterator.java
@@ -33,7 +33,7 @@ import java.util.NoSuchElementException;
public abstract class AbstractIterator<E> implements Iterator<E> {
/**
* The next value to be returned by {@link #next()}, or {@code null} if
not yet determined.
- * This field should be set by a non-null value by {@link #hasNext()},
unless there are no more elements.
+ * This field should be set to a non-null value by {@link #hasNext()},
unless there are no more elements.
*/
protected E next;
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CollectionsExt.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CollectionsExt.java
index 0c259bbd23..fcff2d39c4 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CollectionsExt.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/CollectionsExt.java
@@ -17,6 +17,7 @@
package org.apache.sis.util.internal.shared;
import java.util.*;
+import java.util.function.IntFunction;
import java.lang.reflect.Array;
import org.opengis.util.CodeList;
import org.opengis.parameter.InvalidParameterCardinalityException;
@@ -337,6 +338,27 @@ public final class CollectionsExt {
return JDK19.newLinkedHashSet(count);
}
+ /**
+ * Returns the given collection viewed as an unmodifiable set.
+ * It is caller's responsibility to ensure that the collection contains no
duplicated values.
+ *
+ * @param <E> type of elements in the set.
+ * @param elements the elements to view as a set.
+ * @return the given collection viewed as a set.
+ */
+ public static <E> Set<E> viewAsSet(final Collection<E> elements) {
+ return new AbstractSet<E>() {
+ @Override public int size() {return
elements.size();}
+ @Override public boolean isEmpty() {return
elements.isEmpty();}
+ @Override public boolean contains(Object o) {return
elements.contains(o);}
+ @Override public boolean containsAll(Collection<?> c) {return
elements.containsAll(c);}
+ @Override public Iterator<E> iterator() {return
elements.iterator();}
+ @Override public Object[] toArray() {return
elements.toArray();}
+ @Override public <T> T[] toArray(T[] a) {return
elements.toArray(a);}
+ @Override public <T> T[] toArray(IntFunction<T[]> g) {return
elements.toArray(g);}
+ };
+ }
+
/**
* Returns the specified array as an immutable set, or {@code null} if the
array is null.
* If the given array contains duplicated elements, i.e. elements that are
equal in the