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 162dc3a5c7 Allow to use an `javax.xml.transform.URIResolver` for 
resolving URI to external documents.
162dc3a5c7 is described below

commit 162dc3a5c7621d807ae53812cf8a7de7ea7c3899
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Dec 20 14:54:57 2023 +0100

    Allow to use an `javax.xml.transform.URIResolver` for resolving URI to 
external documents.
---
 .../main/org/apache/sis/xml/Pooled.java            |  4 +-
 .../main/org/apache/sis/xml/ReferenceResolver.java | 60 +++++++++++++++++++---
 .../main/org/apache/sis/xml/XML.java               |  5 +-
 .../apache/sis/xml/util/ExternalLinkHandler.java   | 19 +++----
 4 files changed, 68 insertions(+), 20 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/Pooled.java 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/Pooled.java
index 78b5b6dd0b..3473dc7d1c 100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/Pooled.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/Pooled.java
@@ -575,7 +575,9 @@ abstract class Pooled {
      */
     @Override
     public String toString() {
-        return Strings.toString(getClass(), "baseURI", 
ExternalLinkHandler.getCurrentURI(),
+        final Context current = Context.current();
+        return Strings.toString(getClass(),
+                "baseURI", Context.linkHandler(current).getURI(),
                 "locale", locale, "timezone", timezone,
                 "versionGML", versionGML, "versionMetadata", versionMetadata);
     }
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
index 4e86d6def6..fc98dc2657 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/ReferenceResolver.java
@@ -20,6 +20,7 @@ import java.net.URI;
 import java.util.UUID;
 import java.lang.reflect.Proxy;
 import javax.xml.transform.Source;
+import javax.xml.transform.URIResolver;
 import jakarta.xml.bind.Unmarshaller;
 import org.opengis.metadata.Identifier;
 import org.apache.sis.util.ArgumentChecks;
@@ -55,10 +56,36 @@ public class ReferenceResolver {
      */
     public static final ReferenceResolver DEFAULT = new ReferenceResolver();
 
+    /**
+     * Provider of sources to use for unmarshalling objects referenced by 
links to another document.
+     * It provides the {@code source} argument in {@link 
#resolveExternal(MarshalContext, Source)}.
+     * If {@code null}, a default resolution is done.
+     *
+     * @since 1.5
+     */
+    protected final URIResolver externalSourceResolver;
+
     /**
      * Creates a default {@code ReferenceResolver}. This constructor is for 
subclasses only.
      */
     protected ReferenceResolver() {
+        externalSourceResolver = null;
+    }
+
+    /**
+     * Creates a new resolver which will use the specified provider of sources 
for unmarshalling external documents.
+     * The specified resolver is invoked when a {@code xlink:href} attribute 
is found, and the associated URI value
+     * has a path to an external document. The resolver provides the {@code 
source} argument which will be given to
+     * {@link #resolveExternal(MarshalContext, Source)}. If the specified 
resolver returns {@code null} for a given
+     * {@code xlink:href}, then {@code ReferenceResolver} will try to resolve 
the URI itself.
+     *
+     * @param externalSourceResolver  resolver of sources, or {@code null} for 
letting {@code ReferenceResolver}
+     *        resolving the URIs itself.
+     *
+     * @since 1.5
+     */
+    public ReferenceResolver(final URIResolver externalSourceResolver) {
+        this.externalSourceResolver = externalSourceResolver;
     }
 
     /**
@@ -116,8 +143,9 @@ public class ReferenceResolver {
      *
      * <ul>
      *   <li>If {@code xlink:href} is null or {@linkplain URI#isOpaque() 
opaque}, returns {@code null}.</li>
-     *   <li>Otherwise, if {@code xlink:href} is a {@linkplain 
URI#getFragment() fragment} with no path such
-     *       as {@code "#foo"}, then:
+     *   <li>Otherwise, if the {@code xlink:href} URI is {@linkplain 
URI#isAbsolute() is absolute} or has a
+     *       {@linkplain URI#getPath() path}, delegates to {@link 
#resolveExternal(MarshalContext, Source)}.</li>
+     *   <li>Otherwise, the URI is a {@linkplain URI#getFragment() fragment} 
such as {@code "#foo"}. Then:
      *     <ul>
      *       <li>If an object of class {@code type} with an identifier 
attribute such as {@code gml:id="foo"}
      *           has previously been seen in the same XML document (i.e., 
"foo" is a backward reference),
@@ -126,9 +154,6 @@ public class ReferenceResolver {
      *           Note that it may happen if the {@code xlink:href} is a 
forward reference.</li>
      *     </ul>
      *   </li>
-     *   <li>Otherwise, (URI {@linkplain URI#isAbsolute() is absolute} or has 
a {@linkplain URI#getPath() path}),
-     *       resolve the URI relatively to current document being unmarshalled 
and
-     *       delegate to {@link #resolveExternal(MarshalContext, Source)}.</li>
      * </ul>
      *
      * If an object is found but is not of the class declared in {@code type},
@@ -167,12 +192,26 @@ public class ReferenceResolver {
              * URI to an external document. We let `ExternalLinkHandler` 
decide how to replace relative URI
              * by absolute URI. It may depend on whether user has specified a 
`javax.xml.stream.XMLResolver`
              */
-            final Source source = Context.linkHandler(c).openReader(href);
+            final ExternalLinkHandler handler = Context.linkHandler(c);
+            Source source = null;
+            if (externalSourceResolver != null) {
+                Object base = handler.getURI();
+                if (base != null) {
+                    source = externalSourceResolver.resolve(href.toString(), 
base.toString());
+                }
+            }
+            if (source == null) {
+                source = handler.openReader(href);
+            }
             object = (source != null) ? resolveExternal(context, source) : 
null;
         } catch (Exception e) {
             ExternalLinkHandler.warningOccured(href, e);
             return null;
         }
+        /*
+         * At this point, the referenced object has been fetched.
+         * Verify its validity.
+         */
         if (type.isInstance(object)) {
             return type.cast(object);
         } else {
@@ -197,7 +236,14 @@ public class ReferenceResolver {
      * The default implementation loads the file from the given source if it 
is not in the cache,
      * then returns the object identified by the fragment part of the URI.
      *
-     * <p>The URL of the document to load, if known, should be given by {@link 
Source#getSystemId()}.</p>
+     * <p>The {@code source} argument should have been determined by the 
caller has below:</p>
+     * <ul>
+     *   <li>If an {@link URIResolver} has been specified at construction 
time, delegates to it.</li>
+     *   <li>Otherwise or if the above returned {@code null}, then if the 
source of the current document
+     *       is associated to a {@link javax.xml.stream.XMLResolver}, 
delegates to it.</li>
+     *   <li>Otherwise, the caller tries to resolve the URI itself.</li>
+     * </ul>
+     * The resolved URL, if known, should be available in {@link 
Source#getSystemId()}.
      *
      * @param  context  context (GML version, locale, <i>etc.</i>) of the 
(un)marshalling process.
      * @param  source   source of the document specified by the {@code 
xlink:href} attribute value.
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XML.java 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XML.java
index 304d9723f9..3ca8a02a36 100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XML.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XML.java
@@ -257,7 +257,10 @@ public final class XML extends Static {
      * <ul>
      *   <li>If the reference is of the form {@code xlink:href="#foo"} and an 
object with the {@code gml:id="foo"}
      *       attribute was previously found in the same XML document, then 
that object will be used.</li>
-     *   <li>Otherwise an empty element containing only the values of the 
above-cited attributes is created.</li>
+     *   <li>Otherwise, if {@code xlink:href} references an external document, 
that document is unmarshalled.
+     *       The URI resolution can be controlled with an {@link 
javax.xml.transform.URIResolver} specified
+     *       at construction time.</li>
+     *   <li>Otherwise, an empty element containing only the values of the 
above-cited attributes is created.</li>
      * </ul>
      *
      * Applications can sometimes do better by using some domain-specific 
knowledge, for example by searching in a
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 65f31fb8c2..06d85dd71e 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
@@ -31,7 +31,6 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.Result;
 import javax.xml.transform.Source;
 import javax.xml.transform.stax.StAXSource;
-import org.apache.sis.util.Debug;
 import org.apache.sis.util.internal.Strings;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.xml.ReferenceResolver;
@@ -116,6 +115,14 @@ public class ExternalLinkHandler {
         base = sibling.getSystemId();
     }
 
+    /**
+     * {@return the base URI of the link handler}. The returned object may be 
an instance of
+     * {@link String}, {@link File}, {@link URL} or {@link URI}, or it may be 
{@code null}.
+     */
+    public final Object getURI() {
+        return base;
+    }
+
     /**
      * Resolves the given path as an URI. This method behaves as specified in 
{@link URI#resolve(URI)},
      * with the URI given at construction-time as the base URI. If the given 
path is relative and there
@@ -260,16 +267,6 @@ valid:  if (b != null) {
         };
     }
 
-    /**
-     * {@return the base URI of the link handler in current (un)marshalling 
context}.
-     * This is a helper method for diagnostic purposes only.
-     */
-    @Debug
-    public static Object getCurrentURI() {
-        final var handler = Context.linkHandler(Context.current());
-        return (handler != null) ? handler.base : null;
-    }
-
     /**
      * {@return a string representation of this link handler for debugging 
purposes}.
      */

Reply via email to