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);