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 05930cba1e5e93314508ea83fc455c880a0aef20 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Feb 2 17:12:47 2024 +0100 Resolve relative path of the form "file:something#foo". Before this commit, the "#foo" fragment prevented the resolution. --- .../apache/sis/xml/util/ExternalLinkHandler.java | 43 +++++++++++++--------- .../main/org/apache/sis/xml/util/URISource.java | 13 ++++++- 2 files changed, 38 insertions(+), 18 deletions(-) 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 dd4c91e0dd..a7f98045a9 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 @@ -56,12 +56,21 @@ public class ExternalLinkHandler { * If the conversion fails, then this value is set to {@code null} for avoiding to try again. * * <p>Note that the URI is a path to the sibling document rather than a path to the parent directory. - * This is okay, {@link URI#resolve(URI)} appears to behave as intended for deriving relative paths.</p> + * This is okay, {@link URI#resolve(URI)} appears to behave as intended for deriving relative paths. + * Fragment (the text after '#'), if any, will be ignored.</p> * * @see #resolve(URI) */ private Object base; + /** + * 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, this is {@code null}. + * + * @see #getFragment() + */ + private String fragment; + /** * Creates a new resolver for documents relative to the document in the specified URL. * The given URL can be what StAX, SAX and DOM call {@code systemId}. @@ -77,7 +86,7 @@ public class ExternalLinkHandler { /** * Creates a new resolver for documents relative to the document in the specified file. * - * @param sibling path to the sibling document, or {@code null} if none. + * @param sibling path to the sibling document. */ public ExternalLinkHandler(final File sibling) { base = sibling; @@ -86,20 +95,23 @@ public class ExternalLinkHandler { /** * Creates a new resolver for documents relative to the document at the specified URL. * - * @param sibling URL to the sibling document, or {@code null} if none. + * @param sibling URL to the sibling document. */ public ExternalLinkHandler(final URL sibling) { base = sibling; + fragment = sibling.getRef(); } /** * Creates a new resolver for documents relative to the document read from the specified source. * - * @param sibling source to the sibling document, or {@code null} if none. + * @param sibling source to the sibling document. */ public ExternalLinkHandler(final Source sibling) { if (sibling instanceof URISource) { - base = ((URISource) sibling).document; + final var s = (URISource) sibling; + base = s.document; + fragment = s.fragment; } else { base = sibling.getSystemId(); } @@ -108,7 +120,7 @@ public class ExternalLinkHandler { /** * Creates a new resolver for documents relative to the document written to the specified result. * - * @param sibling result of the sibling document, or {@code null} if none. + * @param sibling result of the sibling document. */ public ExternalLinkHandler(final Result sibling) { base = sibling.getSystemId(); @@ -129,23 +141,20 @@ public class ExternalLinkHandler { * @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); + if (fragment == null) { + final URI uri = getURI(); + if (uri != null) { + fragment = uri.getFragment(); } } - return null; + return fragment; } /** - * {@return the base URI of the link handler}. This is the same value as {@link #getBase()}, + * Returns 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. + * + * @return the base URI of the link handler, or {@code null} if none. */ public final URI getURI() { final Object b = base; 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 a9cec509b1..38aed58dbe 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 @@ -16,6 +16,7 @@ */ package org.apache.sis.xml.util; +import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.io.InputStream; @@ -54,7 +55,17 @@ public final class URISource extends StreamSource { URISource(URI source) throws URISyntaxException { source = source.normalize(); // Build a new URI unconditionally because it also decodes escaped characters. - final URI c = new URI(source.getScheme(), source.getSchemeSpecificPart(), null); + URI c = new URI(source.getScheme(), source.getSchemeSpecificPart(), null); + if (c.isOpaque() && "file".equalsIgnoreCase(c.getScheme())) { + /* + * If the URI is "file:something" without "/" or "///" characters, resolve as an absolute path. + * This special case happens if `IOUtilities.toFileOrURI(String)` did not converted a string to + * a `java.io.File` because it contains a fragment. Since this constructor removed the fragment, + * we can now attempt this conversion again. The result will be an absolute path. This is needed + * for `URI.resolve(URI)` to work. + */ + c = new File(c.getSchemeSpecificPart()).toURI(); + } document = source.equals(c) ? source : c; // Share the existing instance if applicable. fragment = Strings.trimOrNull(source.getFragment()); }