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 71c5b4f09b5dd40cad0ec90c14af80327100f675
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Apr 28 14:34:23 2025 +0200

    Add a `Features.getLinkTargets(PropertyType)` method in complement to 
`getLinkTarget(PropertyType)` method (plural versus singular).
    Bug fix in `SQLStore` where foreigner keys in queries were followed in the 
wrong directions (foreigner keys in tables were okay).
    Minor simplification in exception handling of `DeferredStream` and minor 
javadoc editions.
---
 .../apache/sis/cloud/aws/s3/ClientFileSystem.java  |  2 +-
 .../org/apache/sis/feature/FeatureOperations.java  | 27 ++++++++--------------
 .../main/org/apache/sis/feature/Features.java      | 25 +++++++++++++++++++-
 .../apache/sis/feature/StringJoinOperation.java    | 14 +++++++++++
 .../apache/sis/feature/builder/TypeBuilder.java    |  2 +-
 .../org/apache/sis/storage/sql/feature/Table.java  |  2 +-
 .../org/apache/sis/util/stream/DeferredStream.java | 10 +++-----
 .../sis/util/privy/UnmodifiableArrayList.java      |  3 ++-
 8 files changed, 55 insertions(+), 30 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/ClientFileSystem.java
 
b/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/ClientFileSystem.java
index 4e79d1f194..e28376cf7d 100644
--- 
a/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/ClientFileSystem.java
+++ 
b/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/ClientFileSystem.java
@@ -234,7 +234,7 @@ final class ClientFileSystem extends FileSystem {
      * Returns a filter that matches {@link KeyPath} string representation 
against a given pattern.
      *
      * @param  syntaxAndPattern  filtering criteria of the {@code 
syntax:pattern}.
-     * @return a {@kink KeyPath} matcher for the given pattern.
+     * @return a {@link KeyPath} matcher for the given pattern.
      * @throws PatternSyntaxException if the pattern is invalid.
      * @throws UnsupportedOperationException if the pattern syntax is not 
known to this implementation.
      */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
index 4ab41f8df7..f1ca57ee9a 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
@@ -58,48 +58,39 @@ import org.opengis.filter.Expression;
  *     <th>Map key</th>
  *     <th>Value type</th>
  *     <th>Returned by</th>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>{@value org.apache.sis.feature.AbstractIdentifiedType#NAME_KEY}</td>
  *     <td>{@link GenericName} or {@link String}</td>
  *     <td>{@link AbstractOperation#getName() Operation.getName()} 
(mandatory)</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>{@value 
org.apache.sis.feature.AbstractIdentifiedType#DEFINITION_KEY}</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link AbstractOperation#getDefinition() 
Operation.getDefinition()}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>{@value 
org.apache.sis.feature.AbstractIdentifiedType#DESIGNATION_KEY}</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link AbstractOperation#getDesignation() 
Operation.getDesignation()}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>{@value 
org.apache.sis.feature.AbstractIdentifiedType#DESCRIPTION_KEY}</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link AbstractOperation#getDescription() 
Operation.getDescription()}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>"result.name"</td>
  *     <td>{@link GenericName} or {@link String}</td>
  *     <td>{@link AbstractAttribute#getName() Attribute.getName()} on the 
{@linkplain AbstractOperation#getResult() result}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>"result.definition"</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link DefaultAttributeType#getDefinition() 
Attribute.getDefinition()} on the {@linkplain AbstractOperation#getResult() 
result}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>"result.designation"</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link DefaultAttributeType#getDesignation() 
Attribute.getDesignation()} on the {@linkplain AbstractOperation#getResult() 
result}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>"result.description"</td>
  *     <td>{@link InternationalString} or {@link String}</td>
  *     <td>{@link DefaultAttributeType#getDescription() 
Attribute.getDescription()} on the {@linkplain AbstractOperation#getResult() 
result}</td>
- *   </tr>
- *   <tr>
+ *   </tr><tr>
  *     <td>{@value 
org.apache.sis.referencing.AbstractIdentifiedObject#LOCALE_KEY}</td>
  *     <td>{@link java.util.Locale}</td>
  *     <td>(none)</td>
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
index 32f9da66a3..00ad38f9d1 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/Features.java
@@ -17,6 +17,7 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
+import java.util.List;
 import java.util.Optional;
 import java.util.IdentityHashMap;
 import org.opengis.util.GenericName;
@@ -49,7 +50,7 @@ import org.opengis.feature.PropertyType;
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   0.5
  */
 public final class Features extends Static {
@@ -301,6 +302,28 @@ public final class Features extends Static {
         return Optional.empty();
     }
 
+    /**
+     * If the given property is a link or a compound key, returns the name of 
the referenced properties.
+     * This method is similar to {@link #getLinkTarget(PropertyType)}, except 
that it recognizes also
+     * the operations created by {@link FeatureOperations#compound 
FeatureOperations.compound(…)}.
+     *
+     * @param  property  the property to test, or {@code null} if none.
+     * @return the referenced property names if {@code property} is a link or 
a compound key,
+     *         or an empty list otherwise.
+     *
+     * @see FeatureOperations#compound(Map, String, String, String, 
PropertyType...)
+     *
+     * @since 1.5
+     */
+    public static List<String> getLinkTargets(final PropertyType property) {
+        return getLinkTarget(property).map(List::of).orElseGet(() -> {
+            if (property instanceof StringJoinOperation) {
+                return ((StringJoinOperation) property).getAttributeNames();
+            }
+            return List.of();
+        });
+    }
+
     /**
      * Ensures that all characteristics and property values in the given 
feature are valid.
      * An attribute is valid if it contains a number of values between the
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
index b69708ecd0..703c02faac 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/StringJoinOperation.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
 import java.util.Map;
 import java.util.Set;
 import java.util.Objects;
+import java.util.List;
 import java.io.IOException;
 import java.io.Serializable;
 import org.opengis.parameter.ParameterDescriptorGroup;
@@ -35,6 +36,7 @@ import org.apache.sis.util.privy.CollectionsExt;
 import org.apache.sis.converter.SurjectiveConverter;
 import org.apache.sis.feature.privy.AttributeConvention;
 import org.apache.sis.feature.internal.Resources;
+import org.apache.sis.util.privy.UnmodifiableArrayList;
 import org.apache.sis.util.resources.Errors;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -129,6 +131,9 @@ final class StringJoinOperation extends AbstractOperation {
     /**
      * The name of the properties (attributes of operations producing 
attributes)
      * from which to get the values to concatenate.
+     *
+     * @see #getAttributeNames()
+     * @see #getDependencies()
      */
     private final String[] attributeNames;
 
@@ -289,6 +294,15 @@ final class StringJoinOperation extends AbstractOperation {
         return dependencies;
     }
 
+    /**
+     * Returns the name of the properties from which to get the values to 
concatenate.
+     * This is the same information as {@link #getDependencies()}, only in a 
different
+     * kind of collection.
+     */
+    final List<String> getAttributeNames() {
+        return UnmodifiableArrayList.wrap(attributeNames);
+    }
+
     /**
      * Formats the given value using the given converter. This method is a 
workaround for the presence
      * of the first {@code ?} in {@code ObjectConverter<?,?>}: defining a 
separated method allows us
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
index 38914393d7..fbef910a6d 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/TypeBuilder.java
@@ -186,7 +186,7 @@ public abstract class TypeBuilder implements Localized {
     /**
      * Returns the name of the {@code IdentifiedType} to create, or {@code 
null} if undefined.
      * This method returns the value built from the last call to a {@code 
setName(…)} method,
-     * or a default name or {@code null} if no name has been explicitly 
specified.
+     * or a default name, or {@code null} if no name has been explicitly 
specified.
      *
      * @return the name of the {@code IdentifiedType} to create (may be a 
default name or {@code null}).
      *
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Table.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Table.java
index 808c4b80b7..65dc579abb 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Table.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Table.java
@@ -253,7 +253,7 @@ final class Table extends AbstractFeatureSet {
                 importedKeys[importedKeysCount++] = relation;
                 continue;
             }
-            relation = source.getRelation(xpath, false);
+            relation = source.getRelation(xpath, true);
             if (relation != null) {
                 exportedKeys[exportedKeysCount++] = relation;
                 continue;
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/util/stream/DeferredStream.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/util/stream/DeferredStream.java
index 1d39b8cf7b..027dd0f108 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/util/stream/DeferredStream.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/util/stream/DeferredStream.java
@@ -120,18 +120,12 @@ public abstract class DeferredStream<T> extends 
StreamWrapper<T> {
         try {
             return createSourceIterator();
         } catch (Exception cause) {
-            final BackingStoreException ex;
-            if (cause instanceof BackingStoreException) {
-                ex = (BackingStoreException) cause;
-            } else {
-                ex = new BackingStoreException(cause.getMessage(), 
Exceptions.unwrap(cause));
-            }
+            throw cannotExecute(cause);
             /*
              * The close handler will be invoked later assuming that the user 
created the stream in a
              * `try ... finally` block. We could invoke the close handler here 
as a safety, but we do
              * not do that in order to have more predictable and consistent 
behavior.
              */
-            throw ex;
         }
     }
 
@@ -207,6 +201,8 @@ public abstract class DeferredStream<T> extends 
StreamWrapper<T> {
         final Exception unwrap = Exceptions.unwrap(cause);
         if (unwrap instanceof RuntimeException) {
             return (RuntimeException) unwrap;
+        } else if (cause instanceof RuntimeException) {
+            return (RuntimeException) cause;
         } else {
             return new BackingStoreException(cause.toString(), unwrap);
         }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
index 43152c5e69..be13369e4e 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/UnmodifiableArrayList.java
@@ -58,6 +58,7 @@ import org.apache.sis.util.collection.CheckedContainer;
  *
  * @param <E>  the type of elements in the list.
  */
+@SuppressWarnings("EqualsAndHashcode")
 public class UnmodifiableArrayList<E> extends AbstractList<E> implements 
RandomAccess, CheckedContainer<E>, Serializable {
     /**
      * For compatibility with different versions.
@@ -421,7 +422,7 @@ public class UnmodifiableArrayList<E> extends 
AbstractList<E> implements RandomA
             if (!(object instanceof UnmodifiableArrayList<?>)) {
                 return super.equals(object);
             }
-            final UnmodifiableArrayList<?> that = (UnmodifiableArrayList<?>) 
object;
+            final var that = (UnmodifiableArrayList<?>) object;
             int size  = this.size();
             if (size != that.size()) {
                 return false;

Reply via email to