sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1824166 - in /sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml: FilterVersion.java FilteredEvent.java FilteredReader.java FilteredWriter.java FilteredXML.java InputFactory.java PooledUnmarshaller.java
Date Tue, 13 Feb 2018 18:02:12 GMT
Author: desruisseaux
Date: Tue Feb 13 18:02:12 2018
New Revision: 1824166

URL: http://svn.apache.org/viewvc?rev=1824166&view=rev
Log:
Replace the use of XMLStreamReader by XMLEventReader.

Added:
    sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java   (with props)
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/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/java/org/apache/sis/xml/InputFactory.java
    sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledUnmarshaller.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=1824166&r1=1824165&r2=1824166&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 18:02:12 2018
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Collections;
 import javax.xml.namespace.QName;
+import org.apache.sis.util.Debug;
 import org.apache.sis.internal.jaxb.LegacyNamespaces;
 
 
@@ -153,6 +154,14 @@ final class FilterVersion {
         final String exportProperty(final String localPart) {
             return exports.getOrDefault(localPart, localPart);
         }
+
+        /**
+         * Returns the namespace for debugging purpose.
+         */
+        @Override @Debug
+        public String toString() {
+            return namespace;
+        }
     }
 
     /**
@@ -241,6 +250,10 @@ final class FilterVersion {
                 uri = r.namespace;
                 name = new QName(uri, r.exportProperty(name.getLocalPart()),
                         Namespaces.getPreferredPrefix(uri, name.getPrefix()));
+                /*
+                 * Note: the call for 'getPreferredPrefix' above is required:
+                 * JAXB seems to need the prefixes for recognizing namespaces.
+                 */
             }
         }
         return name;

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=1824166&r1=1824165&r2=1824166&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 18:02:12 2018
@@ -207,7 +207,7 @@ abstract class FilteredEvent<E extends X
      * This wrapper is used for changing the namespace and sometime the name of the element.
      * The attributes may also be modified.
      */
-    static final class Start extends FilteredEvent<StartElement> implements StartElement {
+    static class Start extends FilteredEvent<StartElement> implements StartElement {
         /** The namespaces, may or may not be the same than the wrapped event. */
         private final List<Namespace> namespaces;
 
@@ -215,7 +215,7 @@ abstract class FilteredEvent<E extends X
         private final List<Attribute> attributes;
 
         /** The version to export, used for wrapping namespace context. */
-        private final FilterVersion version;
+        final FilterVersion version;
 
         /** Wraps the given event with potentially different name, namespaces and attributes. */
         Start(StartElement event, QName name, List<Namespace> namespaces, List<Attribute> attributes, FilterVersion version) {
@@ -225,18 +225,18 @@ abstract class FilteredEvent<E extends X
             this.version    = version;
         }
 
-        @Override public boolean             isStartElement() {return true;}
-        @Override public StartElement        asStartElement() {return this;}
-        @Override public int                 getEventType()   {return START_ELEMENT;}
-        @Override public Iterator<Namespace> getNamespaces()  {return namespaces.iterator();}
-        @Override public Iterator<Attribute> getAttributes()  {return attributes.iterator();}
+        @Override public final boolean             isStartElement() {return true;}
+        @Override public final StartElement        asStartElement() {return this;}
+        @Override public final int                 getEventType()   {return START_ELEMENT;}
+        @Override public final Iterator<Namespace> getNamespaces()  {return namespaces.iterator();}
+        @Override public final Iterator<Attribute> getAttributes()  {return attributes.iterator();}
 
         /**
          * Returns the attribute referred to by the given name, or {@code null} if none.
          * Current implementation is okay on the assumption that there is few attributes.
          */
         @Override
-        public Attribute getAttributeByName(final QName name) {
+        public final Attribute getAttributeByName(final QName name) {
             for (final Attribute attr : attributes) {
                 if (name.equals(attr.getName())) {
                     return attr;
@@ -246,7 +246,8 @@ abstract class FilteredEvent<E extends X
         }
 
         /**
-         * Gets a read-only namespace context.
+         * Gets a read-only namespace context. Default implementation is suitable for exports
+         * (i.e. write operation). Needs to be overridden for imports (read operation).
          *
          * @see FilteredWriter#getNamespaceContext()
          */
@@ -257,6 +258,8 @@ abstract class FilteredEvent<E extends X
 
         /**
          * Gets the value that the prefix is bound to in the context of this element.
+         * Default implementation is suitable for export (i.e. write operation).
+         * Needs to be overridden for imports (read operation).
          */
         @Override
         public String getNamespaceURI(final String prefix) {
@@ -267,7 +270,7 @@ abstract class FilteredEvent<E extends X
          * Writes the event as per the XML 1.0 without indentation or whitespace.
          */
         @Override
-        void write(final Appendable out) throws IOException {
+        final void write(final Appendable out) throws IOException {
             name(out.append('<'));
             final int n = attributes.size();
             for (int i=0; i<n; i++) {
@@ -277,4 +280,30 @@ abstract class FilteredEvent<E extends X
             out.append('>');
         }
     }
+
+    /**
+     * Wrapper over an element emitted during the reading of an XML document.
+     */
+    static final class Import extends Start {
+        /** Wraps the given event with potentially different name, namespaces and attributes. */
+        Import(StartElement event, QName name, List<Namespace> namespaces, List<Attribute> attributes, FilterVersion version) {
+            super(event, name, namespaces, attributes, version);
+        }
+
+        /**
+         * Gets a read-only namespace context.
+         */
+        @Override
+        public NamespaceContext getNamespaceContext() {
+            return FilteredNamespaces.importNS(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) {
+            return version.importNS(event.getNamespaceURI(prefix));
+        }
+    }
 }

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=1824166&r1=1824165&r2=1824166&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 18:02:12 2018
@@ -22,23 +22,30 @@ import java.util.List;
 import java.util.HashSet;
 import java.util.HashMap;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.InvalidPropertiesFormatException;
 import java.io.IOException;
 import java.io.LineNumberReader;
 import java.io.InputStreamReader;
-import java.util.InvalidPropertiesFormatException;
 import javax.xml.namespace.QName;
-import javax.xml.namespace.NamespaceContext;
 import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.stream.util.StreamReaderDelegate;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.events.XMLEvent;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Namespace;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.CollectionsExt;
 
+import static javax.xml.stream.XMLStreamConstants.*;
+
 
 /**
  * A filter replacing the namespaces found in XML documents by the namespaces expected by SIS at unmarshalling time.
- * This class forwards every method calls to the wrapped {@link XMLStreamReader}, but with some {@code namespaceURI}
+ * This class forwards every method calls to the wrapped {@link XMLEventReader}, but with some {@code namespaceURI}
  * modified before being transfered. This class uses a dictionary for identifying the XML namespaces expected by JAXB
  * implementation. This is needed when a single namespace in a legacy schema has been splitted into many namespaces
  * in the newer schema. This happen for example in the upgrade from ISO 19139:2007 to ISO 19115-3.
@@ -50,7 +57,7 @@ import org.apache.sis.internal.util.Coll
  * @since   1.0
  * @module
  */
-final class FilteredReader extends StreamReaderDelegate {
+final class FilteredReader extends FilteredXML implements XMLEventReader {
     /**
      * Location of the file listing types and attributes contained in namespaces.
      * The file location is relative to this {@code NamespaceContent} class.
@@ -164,6 +171,18 @@ final class FilteredReader extends Strea
     }
 
     /**
+     * Returns the namespace for the given ISO type, or {@code null} if unknown.
+     * This is the namespace used in JAXB annotations.
+     *
+     * @param  type  a class name defined by ISO 19115 or related standards (e.g. {@code "CI_Citation"}.
+     * @return a namespace for the given type, or {@code null} if unknown.
+     */
+    static String namespace(final String type) {
+        final Map<String,String> attributes = NAMESPACES.get(type);
+        return (attributes != null) ? attributes.get(TYPE_KEY) : null;
+    }
+
+    /**
      * The mapping from attribute names to types where such attribute is declared.
      * An attribute of the same name may be declared in many types.
      *
@@ -194,105 +213,146 @@ final class FilteredReader extends Strea
     }
 
     /**
-     * The external XML format version to unmarshal from.
+     * The reader from which to read events.
      */
-    private final FilterVersion version;
+    private final XMLEventReader in;
 
     /**
-     * List of encountered XML tags, in order. Used for backtracking.
-     * Elements are removed from this list when they are closed.
+     * List of encountered XML tags, in order. Used for backtracking. Elements are removed from this list
+     * when they are closed. Names should be the ones we get after conversion from namespaces used in XML
+     * document to namespaces used in JAXB annotations.
      */
-    private final List<String> outerElements;
+    private final List<QName> 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"}.
+     * Creates a new filter for the given version of the standards.
      */
-    private String renamed;
+    FilteredReader(final XMLEventReader in, final FilterVersion version) {
+        super(version);
+        this.in = in;
+        outerElements = new ArrayList<>();
+    }
 
     /**
-     * Creates a new filter for the given version of the standards.
+     * Checks if there are more events.
      */
-    FilteredReader(final XMLStreamReader in, final FilterVersion version) {
-        super(in);
-        this.version = version;
-        outerElements = new ArrayList<>();
+    @Override
+    public boolean hasNext() {
+        return in.hasNext();
     }
 
     /**
-     * Forwards the call and keep trace of the XML element opened up to this point.
+     * Check the next XMLEvent without reading it from the stream.
      */
     @Override
-    public int next() throws XMLStreamException {
-        return traceElements(super.next());
+    public XMLEvent peek() throws XMLStreamException {
+        return convert(in.peek(), false);
     }
 
     /**
-     * Forwards the call and keep trace of the XML element opened up to this point.
+     * Returns the next element. Use {@link #nextEvent()} instead.
      */
     @Override
-    public int nextTag() throws XMLStreamException {
-        return traceElements(super.nextTag());
+    public Object next() {
+        return convert((XMLEvent) in.next(), true);
     }
 
     /**
-     * Keeps trace of XML elements opened up to this point.
-     *
-     * @param  type  value of {@link #getEventType()}.
-     * @return {@code type}, returned for convenience.
+     * Forwards the call and keep trace of the XML elements opened up to this point.
      */
-    private int traceElements(final int type) {
-        switch (type) {
+    @Override
+    public XMLEvent nextEvent() throws XMLStreamException {
+        return convert(in.nextEvent(), true);
+    }
+
+    /**
+     * Forwards the call and keep trace of the XML elements opened up to this point.
+     */
+    @Override
+    public XMLEvent nextTag() throws XMLStreamException {
+        return convert(in.nextTag(), true);
+    }
+
+    /**
+     * Keeps trace of XML elements opened up to this point and imports the given event.
+     * This method replaces the namespaces used in XML document by the namespace used by JAXB annotations.
+     *
+     * @param  event  the event read from the underlying event reader.
+     * @param  next   {@code true} for a {@code next} operation, or {@code false} for a {@code peek} operation.
+     * @return the converted event (may be the same instance).
+     */
+    @SuppressWarnings("unchecked")      // TODO: remove on JDK9
+    private XMLEvent convert(XMLEvent event, final boolean next) {
+        switch (event.getEventType()) {
+            case ATTRIBUTE: {
+                event = convert((Attribute) event);
+                break;
+            }
+            case NAMESPACE: {
+                event = importNS((Namespace) event);
+                break;
+            }
             case START_ELEMENT: {
-                outerElements.add(getLocalName());
+                final StartElement e = event.asStartElement();
+                final QName originalName = e.getName();
+                final QName name = convert(originalName);
+                boolean changed = name != originalName;
+                for (final Iterator<Attribute> it = e.getAttributes(); it.hasNext();) {
+                    final Attribute a = it.next();
+                    final Attribute ae = convert(a);
+                    changed |= (a != ae);
+                    renamedAttributes.add(ae);
+                }
+                final List<Namespace> namespaces = importNS(e.getNamespaces(), changed);
+                if (namespaces != null) {
+                    event = new FilteredEvent.Import(e, name, namespaces, attributes(), version);
+                } else {
+                    renamedAttributes.clear();
+                }
+                if (next) {
+                    outerElements.add(e.getName());
+                }
                 break;
             }
             case END_ELEMENT: {
+                final EndElement e = event.asEndElement();
+                final QName originalName = e.getName();
+                final QName name = convert(originalName);
+                final List<Namespace> namespaces = importNS(e.getNamespaces(), name != originalName);
+                if (namespaces != null) {
+                    event = new FilteredEvent.End(e, name, namespaces);
+                }
                 /*
-                 * If this is an end element, close the last open one with a matching name.
-                 * It should be the last list element in a well-formed XML, but we loop in
-                 * the list anyway as a safety.
+                 * Close the last start element with a matching name. It should be the last element
+                 * on the list in a well-formed XML, but we loop in the list anyway as a safety.
                  */
-                final String name = getLocalName();
-                for (int i = outerElements.size(); --i >= 0;) {
-                    if (name.equals(outerElements.get(i))) {
-                        outerElements.remove(i);
-                        break;
+                if (next) {
+                    for (int i = outerElements.size(); --i >= 0;) {
+                        if (name.equals(outerElements.get(i))) {
+                            outerElements.remove(i);
+                            break;
+                        }
                     }
                 }
                 break;
             }
         }
-        return type;
-    }
-
-    /**
-     * Returns the namespace of the given ISO type, or {@code null} if unknown.
-     * This is the namespace used in JAXB annotations.
-     *
-     * @param  type  a class name defined by ISO 19115 or related standards (e.g. {@code "CI_Citation"}.
-     * @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;
+        return event;
     }
 
     /**
-     * Return the namespace used by implementation (the SIS classes with JAXB annotations)
-     * in the context of the current part of the XML document being read.
+     * Imports a name read from the XML document to the name to give to JAXB.
+     * The new namespace depends on both the old namespace and the element name.
+     * The prefix is left unchanged since it can be arbitrary (even if confusing for
+     * human reader used to ISO/TC211 prefixes, it is non-ambiguous to the computer).
      *
-     * @param   localPart   the local name of the element or attribute currently being read.
+     * @param   name   the name of the element or attribute currently being read.
      * @return  the namespace URI for the element or attribute in the current context (e.g. an ISO 19115-3 namespace),
      *          or {@code null} if the given name is unknown.
      */
-    private String namespaceOf(final String localPart) {
-        renamed = localPart;
+    private QName convert(QName name) {
+        String namespace = null;                        // In this method, null means no change to given name.
+        String localPart = name.getLocalPart();
         final Set<String> declaringTypes = DECLARING_TYPES.get(localPart);
         if (declaringTypes == null) {
             /*
@@ -302,7 +362,7 @@ final class FilteredReader extends Strea
              */
             final Map<String,String> attributes = NAMESPACES.get(localPart);
             if (attributes != null) {
-                return attributes.get(TYPE_KEY);
+                namespace = attributes.get(TYPE_KEY);           // May be null.
             }
         } else {
             /*
@@ -310,105 +370,117 @@ final class FilteredReader extends Strea
              * possible parent element. Then, we use the namespace associated with that parent.
              */
             for (int i = outerElements.size(); --i >= 0;) {
-                final String parent = outerElements.get(i);
+                final String parent = outerElements.get(i).getLocalPart();
                 if (declaringTypes.contains(parent)) {
                     /*
                      * A NullPointerException below would be a bug in our algorithm because
                      * we constructed DECLARING_TYPES from NAMESPACES keys only.
                      */
                     final Map<String,String> attributes = NAMESPACES.get(parent);
-                    String uri = attributes.get(localPart);
-                    if (!isNamespace(uri)) {
-                        renamed = uri;
-                        uri = attributes.get(uri);
+                    namespace = attributes.get(localPart);
+                    if (!isNamespace(namespace)) {
+                        localPart = namespace;
+                        namespace = attributes.get(namespace);
                     }
-                    return uri;
+                    break;
                 }
             }
         }
-        return null;
-    }
-
-    /**
-     * Converts a name read from the XML document to the name to give to JAXB.
-     * The new namespace depends on both the old namespace and the element name.
-     */
-    private QName importNS(QName name) {
-        final String namespaceURI = name.getNamespaceURI();
-        final String localPart = name.getLocalPart();
-        String replacement = namespaceOf(localPart);
-        if (replacement == null) {
-            replacement = version.importNS(namespaceURI);
+        /*
+         * If above code found no namespace by looking in our dictionary of special cases,
+         * maybe there is a simple one-to-one relationship between the old and new namespaces.
+         * Otherwise we leave the namespace unchanged.
+         */
+        final String oldNS = name.getNamespaceURI();
+        if (namespace == null) {
+            namespace = version.importNS(oldNS);
         }
-        if (!replacement.equals(namespaceURI) || !localPart.equals(renamed)) {
-            name = new QName(replacement, renamed, name.getPrefix());
+        /*
+         * Build a new name if any component (URI or local part) changed. Do not specify the prefix
+         * because the same URI in the XML document could map to many different prefixes after we
+         * converted to the namespaces used by JAXB. Having the same prefix for those different URI
+         * may be a source of confusion.
+         */
+        if (!namespace.equals(oldNS) || !localPart.equals(name.getLocalPart())) {
+            name = new QName(namespace, localPart);
         }
         return name;
     }
 
-    /** Replaces the given URI if needed, then forwards the call. */
-    @Override
-    public void require(final int type, final String namespaceURI, final String localName) throws XMLStreamException {
-        super.require(type, version.exportNS(namespaceURI), localName);
-    }
-
-    /** Returns the context of the underlying reader wrapped in a filter that converts the namespaces on the fly. */
-    @Override
-    public NamespaceContext getNamespaceContext() {
-        return FilteredNamespaces.importNS(super.getNamespaceContext(), version);
-    }
-
-    /** Forwards the call, then replaces the namespace URI if needed. */
-    @Override
-    public QName getName() {
-        return importNS(super.getName());
-    }
-
-    /** Forwards the call, then replaces the namespace URI if needed. */
-    @Override
-    public QName getAttributeName(final int index) {
-        return importNS(super.getAttributeName(index));
+    /**
+     * Imports an attribute read from the XML document.
+     * If there is no name change, then this method returns the given instance as-is.
+     */
+    private Attribute convert(Attribute attribute) {
+        final QName originalName = attribute.getName();
+        final QName name = convert(originalName);
+        if (name != originalName) {
+            attribute = new FilteredEvent.Attr(attribute, name);
+        }
+        return attribute;
     }
 
     /**
-     * Returns the namespace of current element, after replacement by the URI used by SIS.
-     * This replacement depends on the current local name in addition of current namespace.
+     * Imports a namespace read from the XML document. This may imply a prefix change.
+     * If there is no namespace change, then this method returns the given instance as-is.
+     *
+     * @param  namespace  the namespace to import.
      */
-    @Override
-    public String getNamespaceURI() {
-        String namespace = namespaceOf(getLocalName());
-        if (namespace == null) {
-            namespace = version.importNS(super.getNamespaceURI());
+    private Namespace importNS(final Namespace namespace) {
+        String uri = namespace.getNamespaceURI();
+        if (uri != null && !uri.isEmpty()) {
+            uri = FilteredXML.removeTrailingSlash(uri);
+            final String imported = version.importNS(uri);
+            if (imported != uri) {
+                return new FilteredEvent.NS(namespace, Namespaces.getPreferredPrefix(imported, namespace.getPrefix()), imported);
+            }
         }
         return namespace;
     }
 
     /**
-     * Forwards the call, then replaces the returned URI if needed.
+     * Imports the namespaces read from the XML document.
      *
-     * <b>Note:</b> the index passed to this method is the index of a namespace declaration on the root element.
-     * This should not matter as long as each <em>element</em> has the proper namespace URI.
-     */
-    @Override
-    public String getNamespaceURI(int index) {
-        return version.importNS(super.getNamespaceURI(index));
+     * @param  namespaces  the namespaces to filter.
+     * @param  changed     whether to unconditionally pretend that there is a change.
+     * @return the updated namespaces, or {@code null} if there is no changes.
+     */
+    private List<Namespace> importNS(final Iterator<Namespace> namespaces, boolean changed) {
+        if (!namespaces.hasNext()) {
+            return changed ? Collections.emptyList() : null;
+        }
+        final List<Namespace> modified = new ArrayList<>();
+        do {
+            Namespace namespace = namespaces.next();
+            changed |= (namespace != (namespace = importNS(namespace)));
+            modified.add(namespace);
+        } while (namespaces.hasNext());
+        return changed ? modified : null;
     }
 
-    /** Forwards the call, then replaces the returned URI if needed. */
+    /**
+     * Reads the content of a text-only element. Forwards from the underlying reader as-is.
+     */
     @Override
-    public String getNamespaceURI(final String prefix) {
-        return version.importNS(super.getNamespaceURI(prefix));
+    public String getElementText() throws XMLStreamException {
+        return in.getElementText();
     }
 
-    /** Forwards the call, then replaces the returned URI if needed. */
+    /**
+     * Get the value of a feature/property from the underlying implementation.
+     */
     @Override
-    public String getAttributeNamespace(final int index) {
-        return version.importNS(super.getAttributeNamespace(index));
+    public Object getProperty​(final String name) {
+        return in.getProperty(name);
     }
 
-    /** Replaces the given URI if needed, then forwards the call. */
+    /**
+     * Frees any resources associated with this reader.
+     * This method does not close the underlying input source.
+     */
     @Override
-    public String getAttributeValue(final String namespaceUri, final String localName) {
-        return super.getAttributeValue(version.exportNS(namespaceUri), localName);
+    public void close() throws XMLStreamException {
+        outerElements.clear();
+        in.close();
     }
 }

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=1824166&r1=1824165&r2=1824166&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 18:02:12 2018
@@ -19,7 +19,6 @@ package org.apache.sis.xml;
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
@@ -51,18 +50,13 @@ import static javax.xml.stream.XMLStream
  * @since   1.0
  * @module
  */
-final class FilteredWriter implements XMLEventWriter {
+final class FilteredWriter extends FilteredXML implements XMLEventWriter {
     /**
      * Where events are sent.
      */
     private final XMLEventWriter out;
 
     /**
-     * The other version to marshal to.
-     */
-    private final FilterVersion version;
-
-    /**
      * Keep track of namespace URIs that have already been declared so they don't get duplicated.
      * This map is recycled in two different contexts:
      *
@@ -74,19 +68,12 @@ final class FilteredWriter implements XM
     private final Map<String, Namespace> uniqueNamespaces;
 
     /**
-     * Temporary list of attributes after their namespace change.
-     * This list is recycled for each XML element to be read.
-     */
-    private final List<Attribute> exportedAttributes;
-
-    /**
      * Creates a new filter for the given version of the standards.
      */
     FilteredWriter(final XMLEventWriter out, final FilterVersion version) {
+        super(version);
         this.out = out;
-        this.version = version;
         uniqueNamespaces = new LinkedHashMap<>();
-        exportedAttributes = new ArrayList<>();
     }
 
     /**
@@ -113,11 +100,7 @@ final class FilteredWriter implements XM
     private Namespace exportIfNew(final Namespace namespace) {
         String uri = namespace.getNamespaceURI();
         if (uri != null && !uri.isEmpty()) {
-            final int end = uri.length() - 1;
-            if (uri.charAt(end) == '/') {
-                uri = uri.substring(0, end);                            // Trim trailing '/' in URI.
-            }
-            final String exported = version.exportNS(uri);
+            final String exported = version.exportNS(removeTrailingSlash(uri));
             if (exported != uri) {
                 return uniqueNamespaces.computeIfAbsent(exported, (k) -> {
                     return new FilteredEvent.NS(namespace, Namespaces.getPreferredPrefix(k, namespace.getPrefix()), k);
@@ -188,21 +171,13 @@ final class FilteredWriter implements XM
                     final Attribute a = it.next();
                     final Attribute ae = export(a);
                     changed |= (a != ae);
-                    exportedAttributes.add(ae);
+                    renamedAttributes.add(ae);
                 }
                 final List<Namespace> namespaces = export(e.getNamespaces(), changed);
                 if (namespaces != null) {
-                    final List<Attribute> attributes;
-                    switch (exportedAttributes.size()) {
-                        case 0:  attributes = Collections.emptyList(); break;      // Avoid object creation for this common case.
-                        case 1:  attributes = Collections.singletonList(exportedAttributes.remove(0)); break;
-                        default: attributes = Arrays.asList(exportedAttributes.toArray(new Attribute[exportedAttributes.size()]));
-                                 exportedAttributes.clear();
-                                 break;
-                    }
-                    event = new FilteredEvent.Start(e, name, namespaces, attributes, version);
+                    event = new FilteredEvent.Start(e, name, namespaces, attributes(), version);
                 } else {
-                    exportedAttributes.clear();
+                    renamedAttributes.clear();
                 }
                 break;
             }

Added: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java?rev=1824166&view=auto
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java (added)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java [UTF-8] Tue Feb 13 18:02:12 2018
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.xml;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import javax.xml.stream.events.Attribute;
+
+
+/**
+ * Base class of XML reader or writer replacing the namespaces used by JAXB by namespaces used in the XML document,
+ * or conversely (depending on the direction of the I/O operation).
+ *
+ * See {@link FilteredNamespaces} for more information.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @author  Cullen Rombach (Image Matters)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+abstract class FilteredXML {
+    /**
+     * The external XML format version to (un)marshal from.
+     */
+    final FilterVersion version;
+
+    /**
+     * Temporary list of attributes after their namespace change.
+     * This list is recycled for each XML element to be read or written.
+     */
+    final List<Attribute> renamedAttributes;
+
+    /**
+     * Creates a new XML reader or writer.
+     */
+    FilteredXML(final FilterVersion version) {
+        this.version = version;
+        renamedAttributes = new ArrayList<>();
+    }
+
+    /**
+     * Removes the trailing slash in given URI, if any. It is caller's responsibility
+     * to ensure that the URI is not null and not empty before to invoke this method.
+     */
+    static String removeTrailingSlash(String uri) {
+        final int end = uri.length() - 1;
+        if (uri.charAt(end) == '/') {
+            uri = uri.substring(0, end);
+        }
+        return uri;
+    }
+
+    /**
+     * Returns a snapshot of {@link #renamedAttributes} list and clears the later.
+     */
+    final List<Attribute> attributes() {
+        final List<Attribute> attributes;
+        switch (renamedAttributes.size()) {
+            case 0:  attributes = Collections.emptyList(); break;      // Avoid object creation for this common case.
+            case 1:  attributes = Collections.singletonList(renamedAttributes.remove(0)); break;
+            default: attributes = Arrays.asList(renamedAttributes.toArray(new Attribute[renamedAttributes.size()]));
+                     renamedAttributes.clear();
+                     break;
+        }
+        return attributes;
+    }
+}

Propchange: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredXML.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/InputFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/InputFactory.java?rev=1824166&r1=1824165&r2=1824166&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/InputFactory.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/InputFactory.java [UTF-8] Tue Feb 13 18:02:12 2018
@@ -37,7 +37,7 @@ import org.apache.sis.util.Static;
  * only when first needed, when initializing this class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -55,7 +55,7 @@ final class InputFactory extends Static
 
     /*
      * Do not provide convenience method for java.io.File, because the caller needs to close the created
-     * input stream himself (this is not done by XMLStreamReader.close(), despite its method name).
+     * input stream himself (this is not done by XMLEventReader.close(), despite its method name).
      */
 
     /**
@@ -65,8 +65,8 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final InputStream in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(in);
+    public static XMLEventReader createXMLEventReader(final InputStream in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(in);
     }
 
     /**
@@ -76,8 +76,8 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final Reader in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(in);
+    public static XMLEventReader createXMLEventReader(final Reader in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(in);
     }
 
     /**
@@ -87,8 +87,8 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final InputSource in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(new SAXSource(in));
+    public static XMLEventReader createXMLEventReader(final InputSource in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(new SAXSource(in));
     }
 
     /**
@@ -98,8 +98,8 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final XMLEventReader in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(new StAXSource(in));
+    public static XMLEventReader createXMLEventReader(final XMLStreamReader in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(new StAXSource(in));
     }
 
     /**
@@ -109,8 +109,8 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final Node in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(new DOMSource(in));
+    public static XMLEventReader createXMLEventReader(final Node in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(new DOMSource(in));
     }
 
     /**
@@ -120,7 +120,7 @@ final class InputFactory extends Static
      * @return the reader.
      * @throws XMLStreamException if the reader can not be created.
      */
-    public static XMLStreamReader createXMLStreamReader(final Source in) throws XMLStreamException {
-        return FACTORY.createXMLStreamReader(in);
+    public static XMLEventReader createXMLEventReader(final Source in) throws XMLStreamException {
+        return FACTORY.createXMLEventReader(in);
     }
 }

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledUnmarshaller.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledUnmarshaller.java?rev=1824166&r1=1824165&r2=1824166&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledUnmarshaller.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledUnmarshaller.java [UTF-8] Tue Feb 13 18:02:12 2018
@@ -55,7 +55,7 @@ import org.apache.sis.internal.jaxb.Cont
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.4
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -117,7 +117,7 @@ final class PooledUnmarshaller extends P
      * @param  version  identify the namespace substitutions to perform.
      * @return the unmarshalled object.
      */
-    private Object unmarshal(XMLStreamReader input, final FilterVersion version)
+    private Object unmarshal(XMLEventReader input, final FilterVersion version)
             throws XMLStreamException, JAXBException
     {
         input = new FilteredReader(input, version);
@@ -133,10 +133,10 @@ final class PooledUnmarshaller extends P
     }
 
     /**
-     * Same as {@link #unmarshal(XMLStreamReader, FilterVersion)}, but delegating to the unmarshaller
+     * Same as {@link #unmarshal(XMLEventReader, FilterVersion)}, but delegating to the unmarshaller
      * methods returning a JAXB element instead than the one returning the object.
      */
-    private <T> JAXBElement<T> unmarshal(XMLStreamReader input, final FilterVersion version, final Class<T> declaredType)
+    private <T> JAXBElement<T> unmarshal(XMLEventReader input, final FilterVersion version, final Class<T> declaredType)
             throws XMLStreamException, JAXBException
     {
         input = new FilteredReader(input, version);
@@ -158,7 +158,7 @@ final class PooledUnmarshaller extends P
     public Object unmarshal(final InputStream input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -179,7 +179,7 @@ final class PooledUnmarshaller extends P
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
             try (InputStream s = input.openStream()) {
-                return unmarshal(InputFactory.createXMLStreamReader(s), version);
+                return unmarshal(InputFactory.createXMLEventReader(s), version);
             }
         } catch (IOException | XMLStreamException e) {
             throw new JAXBException(e);
@@ -201,7 +201,7 @@ final class PooledUnmarshaller extends P
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
             try (InputStream s = new BufferedInputStream(new FileInputStream(input))) {
-                return unmarshal(InputFactory.createXMLStreamReader(s), version);
+                return unmarshal(InputFactory.createXMLEventReader(s), version);
             }
         } catch (IOException | XMLStreamException e) {
             throw new JAXBException(e);
@@ -222,7 +222,7 @@ final class PooledUnmarshaller extends P
     public Object unmarshal(final Reader input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -242,7 +242,7 @@ final class PooledUnmarshaller extends P
     public Object unmarshal(final InputSource input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -262,7 +262,7 @@ final class PooledUnmarshaller extends P
     public Object unmarshal(final Node input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -282,7 +282,7 @@ final class PooledUnmarshaller extends P
     public <T> JAXBElement<T> unmarshal(final Node input, final Class<T> declaredType) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version, declaredType);
+            return unmarshal(InputFactory.createXMLEventReader(input), version, declaredType);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -302,7 +302,7 @@ final class PooledUnmarshaller extends P
     public Object unmarshal(final Source input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -322,7 +322,7 @@ final class PooledUnmarshaller extends P
     public <T> JAXBElement<T> unmarshal(final Source input, final Class<T> declaredType) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version, declaredType);
+            return unmarshal(InputFactory.createXMLEventReader(input), version, declaredType);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -339,44 +339,10 @@ final class PooledUnmarshaller extends P
      * Delegates the unmarshalling to the wrapped unmarshaller.
      */
     @Override
-    public Object unmarshal(XMLStreamReader input) throws JAXBException {
-        final FilterVersion version = getFilterVersion();
-        if (version != null) {
-            input = new FilteredReader(input, version);
-        }
-        final Context context = begin();
-        try {
-            return unmarshaller.unmarshal(input);
-        } finally {
-            context.finish();
-        }
-    }
-
-    /**
-     * Delegates the unmarshalling to the wrapped unmarshaller.
-     */
-    @Override
-    public <T> JAXBElement<T> unmarshal(XMLStreamReader input, final Class<T> declaredType) throws JAXBException {
-        final FilterVersion version = getFilterVersion();
-        if (version != null) {
-            input = new FilteredReader(input, version);
-        }
-        final Context context = begin();
-        try {
-            return unmarshaller.unmarshal(input, declaredType);
-        } finally {
-            context.finish();
-        }
-    }
-
-    /**
-     * Delegates the unmarshalling to the wrapped unmarshaller.
-     */
-    @Override
-    public Object unmarshal(final XMLEventReader input) throws JAXBException {
+    public Object unmarshal(final XMLStreamReader input) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version);
+            return unmarshal(InputFactory.createXMLEventReader(input), version);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -393,10 +359,10 @@ final class PooledUnmarshaller extends P
      * Delegates the unmarshalling to the wrapped unmarshaller.
      */
     @Override
-    public <T> JAXBElement<T> unmarshal(final XMLEventReader input, final Class<T> declaredType) throws JAXBException {
+    public <T> JAXBElement<T> unmarshal(final XMLStreamReader input, final Class<T> declaredType) throws JAXBException {
         final FilterVersion version = getFilterVersion();
         if (version != null) try {
-            return unmarshal(InputFactory.createXMLStreamReader(input), version, declaredType);
+            return unmarshal(InputFactory.createXMLEventReader(input), version, declaredType);
         } catch (XMLStreamException e) {
             throw new JAXBException(e);
         } else {
@@ -409,6 +375,40 @@ final class PooledUnmarshaller extends P
         }
     }
 
+    /**
+     * Delegates the unmarshalling to the wrapped unmarshaller.
+     */
+    @Override
+    public Object unmarshal(XMLEventReader input) throws JAXBException {
+        final FilterVersion version = getFilterVersion();
+        if (version != null) {
+            input = new FilteredReader(input, version);
+        }
+        final Context context = begin();
+        try {
+            return unmarshaller.unmarshal(input);
+        } finally {
+            context.finish();
+        }
+    }
+
+    /**
+     * Delegates the unmarshalling to the wrapped unmarshaller.
+     */
+    @Override
+    public <T> JAXBElement<T> unmarshal(XMLEventReader input, final Class<T> declaredType) throws JAXBException {
+        final FilterVersion version = getFilterVersion();
+        if (version != null) {
+            input = new FilteredReader(input, version);
+        }
+        final Context context = begin();
+        try {
+            return unmarshaller.unmarshal(input, declaredType);
+        } finally {
+            context.finish();
+        }
+    }
+
     /**
      * Delegates to the wrapped unmarshaller.
      */



Mime
View raw message