Author: desruisseaux Date: Tue Feb 13 14:56:15 2018 New Revision: 1824147 URL: http://svn.apache.org/viewvc?rev=1824147&view=rev Log: Clarification of FilteredNamespaces role. First test on property renaming.
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java sis/branches/ISO-19115-3/core/sis-utility/src/main/resources/org/apache/sis/xml/NamespaceContent.txt sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -18,6 +18,7 @@ package org.apache.sis.xml; import java.util.Map; import java.util.HashMap; +import java.util.Iterator; import java.util.Collections; import javax.xml.namespace.QName; import org.apache.sis.internal.jaxb.LegacyNamespaces; @@ -92,8 +93,7 @@ final class FilterVersion { /** * GML using the legacy {@code "http://www.opengis.net/gml"} namespace. - * Note that the use of GML 3.2 may imply the use of ISO 19139:2007, - * which requires the use of {@link #ALL}. + * Note that the use of GML 3.2 implies the use of ISO 19139:2007. */ static final FilterVersion GML31 = new FilterVersion(ISO19139); static { @@ -170,11 +170,11 @@ final class FilterVersion { /** * The URI replacements to apply when going from the filtered reader/writer to the * model implemented by Apache SIS. This map is the converse of {@link #exports}. + * It does not contain the map of properties to rename because that map is handled + * by {@link FilteredReader} instead, as part of {@code NamespaceContent.txt} file. * * <p>This map shall not be modified after construction. * We do not wrap in {@link Collections#unmodifiableMap(Map)} for efficiency.</p> - * - * @see #imports() */ private final Map<String, String> imports; @@ -248,38 +248,36 @@ final class FilterVersion { /** * Converts a namespace used in JAXB annotation to the namespace used in XML document. + * Returns the same URI if there is no replacement. */ final String exportNS(final String uri) { final FilterVersion.Replacement r = exports.get(uri); return (r != null) ? r.namespace : uri; } - final Replacement export(final String uri) { - return exports.get(uri); - } - /** * Converts a namespace used in XML document to the namespace used in JAXB annotation. + * Returns the same URI if there is no replacement. */ final String importNS(final String uri) { return imports.getOrDefault(uri, uri); } /** - * Returns the URI replacements to apply when going from the model implemented by Apache SIS to the - * filtered reader/writer. Used only for more sophisticated work than what {@link #exportNS(String)} - * does. Wrapped in a {@link Collections#unmodifiableMap(Map)} for safety. + * Converts a namespace used in JAXB annotation to the namespace used in XML document, + * together with a map of properties to rename. + * Returns {@code null} if there is no replacement. */ - final Map<String, Replacement> exports() { - return Collections.unmodifiableMap(exports); + final Replacement export(final String uri) { + return exports.get(uri); } /** - * Returns the URI replacements to apply when going from the filtered reader/writer to the model implemented - * by Apache SIS. Used only for mor sophisticated work than what {@link #exportNS(String)} does. Wrapped in - * a {@link Collections#unmodifiableMap(Map)} for safety. + * Returns the URI replacements to apply when going from the model implemented by Apache SIS to the + * filtered reader/writer. Used only for more sophisticated work than what {@link #exportNS(String)} does. + * Returned as an iterator for avoiding to expose modifiable map; do not invoke {@link Iterator#remove()}. */ - final Map<String, String> imports() { - return Collections.unmodifiableMap(imports); + final Iterator<Map.Entry<String, Replacement>> exports() { + return exports.entrySet().iterator(); } } Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -245,24 +245,29 @@ abstract class FilteredEvent<E extends X return null; } - /** Gets a read-only namespace context. */ - @Override public NamespaceContext getNamespaceContext() { - final NamespaceContext context = event.getNamespaceContext(); - return (context != null) ? new FilteredNamespaces(context, version) : null; + /** + * Gets a read-only namespace context. + * + * @see FilteredWriter#getNamespaceContext() + */ + @Override + public NamespaceContext getNamespaceContext() { + return FilteredNamespaces.exportNS(event.getNamespaceContext(), version); } - /** Gets the value that the prefix is bound to in the context of this element. */ - @Override public String getNamespaceURI(final String prefix) { - final NamespaceContext context = event.getNamespaceContext(); - if (context != null) { - final String uri = context.getNamespaceURI(prefix); - return version.exportNS(uri); - } - return null; + /** + * Gets the value that the prefix is bound to in the context of this element. + */ + @Override + public String getNamespaceURI(final String prefix) { + return version.exportNS(event.getNamespaceURI(prefix)); } - /** Writes the event as per the XML 1.0 without indentation or whitespace. */ - @Override void write(final Appendable out) throws IOException { + /** + * Writes the event as per the XML 1.0 without indentation or whitespace. + */ + @Override + void write(final Appendable out) throws IOException { name(out.append('<')); final int n = attributes.size(); for (int i=0; i<n; i++) { Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -64,6 +64,51 @@ import javax.xml.XMLConstants; */ class FilteredNamespaces implements NamespaceContext { /** + * Given the context for namespaces used in our JAXB annotations, returns a context working with namespaces + * used in XML document. The returned context converts namespace arguments from XML to JAXB namespaces, and + * converts returned namespaces from JAXB to XML. Prefixes are left unchanged since they can be arbitrary + * (even if not the standard match for a given namespace). + * + * <div class="note"><b>Example:</b> + * for a {@code "http://www.isotc211.org/2005/gmd"} namespace (legacy ISO 19139:2007) given in argument to + * {@link #getPrefixes(String)}, the context convert that namespace to all possible ISO 19115-3 namespaces + * (there is many) and returns the associated prefixes: {@code "mdb"}, {@code "cit"}, <i>etc.</i> + * Conversely given a {@code "mdb"}, {@code "cit"}, <i>etc.</i>, prefix, {@link #getNamespaceURI(String)} + * method returns the above-cited legacy GMD namespace.</div> + * + * This can be used when a {@link javax.xml.stream.XMLStreamWriter} has been created for writing with JAXB + * annotations and we want to expose an {@code XMLStreamReader} for writing a legacy XML document. + */ + static NamespaceContext exportNS(NamespaceContext context, final FilterVersion version) { + if (context != null) { + if (context instanceof Import && ((Import) context).version == version) { + context = ((Import) context).context; + } else { + context = new FilteredNamespaces(context, version); + } + } + return context; + } + + /** + * Given a context for namespaces used in XML document, returns a context working with the namespaces used + * in our JAXB annotations. The returned context converts namespace arguments from JAXB to XML namespaces + * before to delegate to the wrapped context, and converts returned namespaces from XML to JAXB. This can + * be used when a {@link javax.xml.stream.XMLStreamReader} has been created for reading a legacy XML document + * and we want to expose an {@code XMLStreamReader} for JAXB. + */ + static NamespaceContext importNS(NamespaceContext context, final FilterVersion version) { + if (context != null) { + if (context.getClass() == FilteredNamespaces.class && ((FilteredNamespaces) context).version == version) { + context = ((FilteredNamespaces) context).context; + } else { + context = new FilteredNamespaces.Import(context, version); + } + } + return context; + } + + /** * The context to wrap, given by {@link FilteredReader} or {@link FilteredWriter}. * * @see javax.xml.stream.XMLStreamReader#getNamespaceContext() @@ -79,38 +124,24 @@ class FilteredNamespaces implements Name /** * Creates a new namespaces filter for the given target version. */ - FilteredNamespaces(final NamespaceContext context, final FilterVersion version) { + private FilteredNamespaces(final NamespaceContext context, final FilterVersion version) { this.context = context; this.version = version; } /** - * Returns the inverse of {@code FilteredNamespaces}. - */ - NamespaceContext inverse(final FilterVersion target) { - return (version == target) ? context : new Import(context, target); - } - - /** * Substitutes the XML namespaces used in XML documents by namespaces used in JAXB annotations. - * This is used at unmarshalling time for importing legacy documents. + * This is used at unmarshalling time for importing legacy documents, performing the reverse of + * {@link FilteredNamespaces}. The <i>namespace → prefix</i> mapping is simpler because various + * ISO 19115-3 namespaces are mapped to the same legacy {@code "gmd"} prefix. */ - static final class Import extends FilteredNamespaces { - /** - * Creates a new namespaces filter for the given source version. - */ + private static final class Import extends FilteredNamespaces { + /** Creates a new namespaces filter for the given source version. */ Import(final NamespaceContext context, final FilterVersion version) { super(context, version); } /** - * Returns the inverse of this namespace. - */ - @Override NamespaceContext inverse(final FilterVersion target) { - return (version == target) ? context : new FilteredNamespaces(context, target); - } - - /** * Returns the namespace used in JAXB annotations for the given prefix in XML document. * If no unique namespace can be mapped (for example if asking the namespace of legacy * {@code "gmd"} prefix), returns {@link XMLConstants#NULL_NS_URI}. @@ -173,7 +204,9 @@ class FilteredNamespaces implements Name * legacy ISO 19139:2007) is mapped to multiple namespaces in the new ISO 19115-3:2016 or other standard. * In such case, we have to iterate over 'exports' entries until we find an inverse mapping. */ - for (final Map.Entry<String, FilterVersion.Replacement> e : version.exports().entrySet()) { + final Iterator<Map.Entry<String, FilterVersion.Replacement>> it = version.exports(); + while (it.hasNext()) { + final Map.Entry<String, FilterVersion.Replacement> e = it.next(); if (namespaceURI.equals(e.getValue().namespace)) { p = context.getPrefix(e.getKey()); if (p != null) return p; @@ -213,9 +246,11 @@ class FilteredNamespaces implements Name private String next; /** Creates a new iterator for the prefixes associated to the given namespace URI. */ - Prefixes(final NamespaceContext context, final Map<String, FilterVersion.Replacement> exports, final String namespaceURI) { + Prefixes(final NamespaceContext context, final Iterator<Map.Entry<String, FilterVersion.Replacement>> exports, + final String namespaceURI) + { this.context = context; - this.exports = exports.entrySet().iterator(); + this.exports = exports; this.namespaceURI = namespaceURI; } Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -205,6 +205,13 @@ final class FilteredReader extends Strea private final List<String> outerElements; /** + * The {@code localPart} argument given to {@link #namespaceOf(String)}, potentially renamed. + * For example given the {@code "DCP/distributedComputingPlatform"} entry in {@value #FILENAME} file, + * a call to {@code namespaceOf("DCP")} will set this field to {@code "distributedComputingPlatform"}. + */ + private String renamed; + + /** * Creates a new filter for the given version of the standards. */ FilteredReader(final XMLStreamReader in, final FilterVersion version) { @@ -268,6 +275,10 @@ final class FilteredReader extends Strea * @return a namespace for the given type, or {@code null} if unknown. */ static String namespace(final String type) { + /* + * Same implementation than namespaceOf(type) but without DECLARING_TYPES.get(…) + * since that value should alway be null for class names. + */ final Map<String,String> attributes = NAMESPACES.get(type); return (attributes != null) ? attributes.get(TYPE_KEY) : null; } @@ -281,10 +292,13 @@ final class FilteredReader extends Strea * or {@code null} if the given name is unknown. */ private String namespaceOf(final String localPart) { + renamed = localPart; final Set<String> declaringTypes = DECLARING_TYPES.get(localPart); if (declaringTypes == null) { /* * If the element is a root element, return the associated namespace. + * We do not need to check if the value associated to TYPE_KEY is for + * a renaming since it is never the case for that key. */ final Map<String,String> attributes = NAMESPACES.get(localPart); if (attributes != null) { @@ -302,7 +316,13 @@ final class FilteredReader extends Strea * A NullPointerException below would be a bug in our algorithm because * we constructed DECLARING_TYPES from NAMESPACES keys only. */ - return NAMESPACES.get(parent).get(localPart); + final Map<String,String> attributes = NAMESPACES.get(parent); + String uri = attributes.get(localPart); + if (!isNamespace(uri)) { + renamed = uri; + uri = attributes.get(uri); + } + return uri; } } } @@ -320,8 +340,8 @@ final class FilteredReader extends Strea if (replacement == null) { replacement = version.importNS(namespaceURI); } - if (!replacement.equals(namespaceURI)) { - name = new QName(replacement, localPart, name.getPrefix()); + if (!replacement.equals(namespaceURI) || !localPart.equals(renamed)) { + name = new QName(replacement, renamed, name.getPrefix()); } return name; } @@ -335,7 +355,7 @@ final class FilteredReader extends Strea /** Returns the context of the underlying reader wrapped in a filter that converts the namespaces on the fly. */ @Override public NamespaceContext getNamespaceContext() { - return new FilteredNamespaces.Import(super.getNamespaceContext(), version); + return FilteredNamespaces.importNS(super.getNamespaceContext(), version); } /** Forwards the call, then replaces the namespace URI if needed. */ Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -276,20 +276,17 @@ final class FilteredWriter implements XM */ @Override public void setNamespaceContext(NamespaceContext context) throws XMLStreamException { - if (context instanceof FilteredNamespaces) { - context = ((FilteredNamespaces) context).inverse(version); - } else { - context = new FilteredNamespaces.Import(context, version); - } - out.setNamespaceContext(context); + out.setNamespaceContext(FilteredNamespaces.importNS(context, version)); } /** * Returns the context of the underlying writer wrapped in a filter that convert the namespaces on the fly. + * + * @see FilteredEvent.Start#getNamespaceContext() */ @Override public NamespaceContext getNamespaceContext() { - return new FilteredNamespaces(out.getNamespaceContext(), version); + return FilteredNamespaces.exportNS(out.getNamespaceContext(), version); } /** Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/resources/org/apache/sis/xml/NamespaceContent.txt URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/resources/org/apache/sis/xml/NamespaceContent.txt?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/main/resources/org/apache/sis/xml/NamespaceContent.txt [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/main/resources/org/apache/sis/xml/NamespaceContent.txt [UTF-8] Tue Feb 13 14:56:15 2018 @@ -3,6 +3,7 @@ # Lines with zero-spaces indentation are namespace URIs. # Lines with two-spaces indentation are classes. # Lines with four-spaces indentation are attributes. +# old/new means that a property needs to be renamed. # http://standards.iso.org/iso/19115/-3/cit/1.0 AbstractCI_Party @@ -965,11 +966,11 @@ http://standards.iso.org/iso/19115/-3/sr <type> connectPoint dependsOn - distributedComputingPlatform + DCP/distributedComputingPlatform invocationName operationDescription operationName - parameter + parameters/parameter SV_Parameter <type> description Modified: sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java?rev=1824147&r1=1824146&r2=1824147&view=diff ============================================================================== --- sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java [UTF-8] (original) +++ sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java [UTF-8] Tue Feb 13 14:56:15 2018 @@ -51,7 +51,7 @@ public final strictfp class FilteredName */ @Test public void testGetPrefixes() { - final FilteredNamespaces fns = new FilteredNamespaces(this, FilterVersion.ISO19139); + final NamespaceContext fns = FilteredNamespaces.exportNS(this, FilterVersion.ISO19139); final Iterator<String> it = fns.getPrefixes(LegacyNamespaces.GMD); final Set<String> prefixes = new HashSet<>(); while (it.hasNext()) { @@ -91,7 +91,7 @@ public final strictfp class FilteredName */ @Test public void testGetPrefix() { - final FilteredNamespaces fns = new FilteredNamespaces(this, FilterVersion.ISO19139); + final NamespaceContext fns = FilteredNamespaces.exportNS(this, FilterVersion.ISO19139); /* * Following tests are not really interesting since FilteredNamespaces, * after failing to find a mapping, just delegates to this.getPrefix(…).