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 b0f5f0e3660e0f20cb7119e8b3a30211bff74bde
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Feb 1 13:23:28 2024 +0100

    Allow the resolution of relative `xlink:href` from GML files opened as a 
`DataStore`.
    Allow the resolution of URI fragment from `XML.unmarshal(…)`.
---
 .../org/apache/sis/xml/PooledUnmarshaller.java     | 43 +++++++++++++++++-----
 .../main/org/apache/sis/xml/bind/Context.java      |  4 +-
 .../apache/sis/xml/util/ExternalLinkHandler.java   | 21 +++++++++++
 .../main/org/apache/sis/xml/util/URISource.java    | 11 ++++--
 .../main/org/apache/sis/storage/xml/Store.java     | 11 ++++--
 5 files changed, 72 insertions(+), 18 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java
index cab80f94d7..6d328c12ce 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/PooledUnmarshaller.java
@@ -126,7 +126,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         final Context context = begin(linkHandler);
         final Object object;
         try {
-            object = unmarshaller.unmarshal(input);
+            object = resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
         } finally {
             context.finish();
         }
@@ -169,7 +169,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -219,7 +219,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
                 } else {
                     final Context context = begin(linkHandler);
                     try {
-                        return unmarshaller.unmarshal(s);
+                        return resolve(unmarshaller.unmarshal(s), linkHandler, 
context);
                     } finally {
                         context.finish();
                     }
@@ -246,6 +246,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
+                // No need to invoke `resolve(…)` because a file cannot have a 
fragment.
                 return unmarshaller.unmarshal(input);
             } finally {
                 context.finish();
@@ -267,7 +268,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -288,7 +289,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -309,7 +310,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -351,7 +352,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -393,7 +394,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         } else {
             final Context context = begin(linkHandler);
             try {
-                return unmarshaller.unmarshal(input);
+                return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
             } finally {
                 context.finish();
             }
@@ -438,7 +439,7 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         }
         final Context context = begin(linkHandler);
         try {
-            return unmarshaller.unmarshal(input);
+            return resolve(unmarshaller.unmarshal(input), linkHandler, 
context);
         } finally {
             context.finish();
         }
@@ -467,6 +468,30 @@ final class PooledUnmarshaller extends Pooled implements 
Unmarshaller {
         }
     }
 
+    /**
+     * If the input is a fragment, returns the object referenced by the 
fragment.
+     * Otherwise returns the given object unchanged.
+     *
+     * @param  object       the object parsed from the whole document.
+     * @param  linkHandler  the document-dependent resolver of relative URIs, 
or {@code null}.
+     * @param  context      the marshalling context, or {@code null}.
+     * @return object referenced by the fragment, or the given {@code object} 
if no fragment was specified.
+     */
+    private static Object resolve(final Object object, final 
ExternalLinkHandler linkHandler, final Context context) {
+        if (linkHandler != null) {
+            final String fragment = linkHandler.getFragment();
+            if (fragment != null) {
+                final Object r = Context.getObjectForID(context, fragment);
+                if (r != null) {
+                    return r;
+                }
+                Context.warningOccured(context, PooledUnmarshaller.class, 
"unmarshal",
+                        Errors.class, Errors.Keys.NotABackwardReference_1, 
fragment);
+            }
+        }
+        return object;
+    }
+
     /**
      * Returns the exception to throw for an input file or URL that cannot be 
parsed.
      */
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
index 9a915dd71c..ac612f701f 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
@@ -677,10 +677,12 @@ public final class Context extends MarshalContext {
         if (context != null) {
             final Object existing = context.xmlidToObject.putIfAbsent(id, 
object);
             if (existing != null) {
+                // Note: `existing.equals(object)` would be unreliable because 
object construction is unfinished.
                 return existing == object;
             }
             if (context.objectToXmlid.put(object, id) != null) {
-                throw new CorruptedObjectException(id);    // Should never 
happen since all put calls are in this method.
+                // Should never happen because all put calls are in this 
method.
+                throw new CorruptedObjectException(id);
             }
         }
         return true;
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
index 06fdeddf4d..5940fcc9e2 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java
@@ -122,6 +122,27 @@ public class ExternalLinkHandler {
         return base;
     }
 
+    /**
+     * Returns the fragment (without leading dash) from a {@link URL}, {@link 
URI} or {@link CharSequence} instance.
+     * If no fragment is found, or if the {@linkplain #base} does not support 
fragments, return {@code null}.
+     *
+     * @return the fragment in the base URI, or {@code null} if none.
+     */
+    public final String getFragment() {
+        if (base instanceof URI) {
+            return ((URI) base).getFragment();
+        } else if (base instanceof URL) {
+            return ((URL) base).getRef();
+        } else if (base instanceof String) {
+            final var b = (String) base;
+            final int s = b.lastIndexOf('#');
+            if (s >= 0) {
+                return b.substring(s+1);
+            }
+        }
+        return null;
+    }
+
     /**
      * {@return the base URI of the link handler}. This is the same value as 
{@link #getBase()},
      * but converted to an {@link URI} object when first invoked.
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/URISource.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/URISource.java
index ec049dcfaf..e8b59821fc 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/URISource.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/URISource.java
@@ -73,9 +73,11 @@ public final class URISource extends StreamSource {
     }
 
     /**
-     * Creates a new source.
+     * Creates a new source from the given input stream.
+     * The input should not be null, unless it will be specified later
+     * by a call to {@code setInputStream(…)} or {@code setReader(…)}.
      *
-     * @param  input   stream of the XML document.
+     * @param  input   stream of the XML document, or {@code null} if none.
      * @param  source  URL of the XML document, or {@code null} if none.
      * @return the given input stream as a source.
      */
@@ -94,7 +96,7 @@ public final class URISource extends StreamSource {
      * @return the URI, or {@code null} if not applicable for reading the 
document.
      */
     public URI getReadableURI() {
-        return getInputStream() == null ? document : null;
+        return (getInputStream() == null && getReader() == null) ? document : 
null;
     }
 
     /**
@@ -116,6 +118,7 @@ public final class URISource extends StreamSource {
      */
     @Override
     public String toString() {
-        return Strings.toString(getClass(), "document", document, "fragment", 
fragment, "inputStream", getInputStream());
+        return Strings.toString(getClass(), "document", document, "fragment", 
fragment,
+                                "inputStream", getInputStream(), "reader", 
getReader());
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/xml/Store.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/xml/Store.java
index 31f3caf0a4..8d9a26478d 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/xml/Store.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/xml/Store.java
@@ -30,15 +30,16 @@ import org.opengis.util.FactoryException;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.xml.XML;
+import org.apache.sis.xml.util.URISource;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.UnsupportedStorageException;
+import org.apache.sis.storage.base.URIDataStore;
+import org.apache.sis.storage.base.MetadataBuilder;
 import org.apache.sis.storage.event.WarningEvent;
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.system.Loggers;
-import org.apache.sis.storage.base.URIDataStore;
-import org.apache.sis.storage.base.MetadataBuilder;
 import org.apache.sis.referencing.util.DefinitionVerifier;
 import org.apache.sis.setup.OptionKey;
 
@@ -85,11 +86,13 @@ final class Store extends URIDataStore implements Filter {
         super(provider, connector);
         final InputStream in = connector.getStorageAs(InputStream.class);
         if (in != null) {
-            source = new StreamSource(in);
+            source = URISource.create(in, location);
         } else {
             final Reader reader = connector.getStorageAs(Reader.class);
             if (reader != null) {
-                source = new StreamSource(reader);
+                var s = URISource.create(null, location);
+                s.setReader(reader);
+                source = s;
             }
         }
         final Closeable c = input(source);

Reply via email to