Author: desruisseaux
Date: Sun Feb 18 15:44:50 2018
New Revision: 1824672

URL: http://svn.apache.org/viewvc?rev=1824672&view=rev
Log:
More simplification of the algorithm for renaming XML element.
Move the 'convert(QName)' method to the Transformer parent class.

Modified:
    
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/xml/NamespaceContent.java
    
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Transformer.java
    
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingReader.java
    
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingWriter.java

Modified: 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/xml/NamespaceContent.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/xml/NamespaceContent.java?rev=1824672&r1=1824671&r2=1824672&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/xml/NamespaceContent.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-metadata/src/test/java/org/apache/sis/xml/NamespaceContent.java
 [UTF-8] Sun Feb 18 15:44:50 2018
@@ -136,7 +136,7 @@ public final class NamespaceContent {
              */
             final String topLevelTypeName = root.name();
             String classNS = namespace(classe, root.namespace());
-            add(classNS, topLevelTypeName, TransformingReader.TYPE_KEY);
+            add(classNS, topLevelTypeName, Transformer.TYPE_KEY);
             for (;; classNS = namespace(classe, root.namespace())) {
                 for (final Method method : classe.getDeclaredMethods()) {
                     if (!method.isBridge()) {
@@ -170,7 +170,7 @@ public final class NamespaceContent {
                 }
             }
             if (singleton != null) {
-                add(namespace(classe, singleton.namespace()), 
singleton.name(), TransformingReader.TYPE_KEY);
+                add(namespace(classe, singleton.namespace()), 
singleton.name(), Transformer.TYPE_KEY);
             }
         }
     }

Modified: 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Transformer.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Transformer.java?rev=1824672&r1=1824671&r2=1824672&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Transformer.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Transformer.java
 [UTF-8] Sun Feb 18 15:44:50 2018
@@ -96,37 +96,52 @@ abstract class Transformer {
     private static final char RENAME_SEPARATOR = '/';
 
     /**
+     * A sentinel value in files loaded by {@link #load(String)} meaning that 
the identifier stands for the type
+     * name instead than for a property name. This sentinel value is used in 
{@value TransformingReader#FILENAME}
+     * and {@value TransformingWriter#FILENAME} resource files. For example in 
the following:
+     *
+     * {@preformat text
+     *  http://standards.iso.org/iso/19115/-3/cit/1.0
+     *   CI_Citation
+     *    <type>
+     *    title
+     *    alternateTitle
+     * }
+     *
+     * The {@code <type>} sentinel value is substituted by {@code 
CI_Citation}, which means that the {@code Citation}
+     * class itself is in the {@code cit} namespace. This needs to be 
specified explicitely because the properties in
+     * a class are not necessarily in the same namespace than the enclosing 
class.
+     */
+    static final String TYPE_KEY = "<type>";
+
+    /**
      * The external XML format version to (un)marshal from.
      */
     final TransformVersion version;
 
     /**
-     * The outer element name together with its attribute. This is an element 
in a linked list.
+     * List of encountered XML tags, 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. For example given the following XML, this list should 
contains {@code cit:CI_Citation},
+     * {@code cit:date} and {@code cit:CI_Date} (in that order) when the 
(un)marshalling reaches the "…" location.
+     *
+     * {@preformat xml
+     *   <cit:CI_Citation>
+     *     <cit:date>
+     *       <cit:CI_Date>
+     *         …
+     *       </cit:CI_Date>
+     *     </cit:date>
+     *   </cit:CI_Citation>
+     * }
      */
-    private static final class OuterElement {
-        /** The outer element name as used in JAXB annotations. */
-        final QName name;
-
-        /** The attribute namespaces, as one of the values of the map loaded 
by {@link #load(String)}. */
-        final Map<String, String> attributeNS;
-
-        /** The previous outer element (in other words, the parent), or {@code 
null} if none. */
-        final OuterElement parent;
-
-        /** Creates a new outer element. */
-        private OuterElement(final OuterElement parent, final QName name, 
final Map<String, String> attributeNS) {
-            this.parent      = parent;
-            this.name        = name;
-            this.attributeNS = attributeNS;
-        }
-    }
+    private final List<QName> outerElements;
 
     /**
-     * Linked list of encountered XML tags, in reverse 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.
+     * Properties of the last outer elements, or {@code null} if not yet 
determined.
+     * If non-empty, this is one of the values got from the map given in 
argument to {@link #open(QName, Map)}.
      */
-    private OuterElement outerElements;
+    private Map<String,String> outerElementProperties;
 
     /**
      * Temporary list of attributes after their namespace change.
@@ -138,8 +153,22 @@ abstract class Transformer {
      * Creates a new XML reader or writer.
      */
     Transformer(final TransformVersion version) {
-        this.version      = version;
-        renamedAttributes = new ArrayList<>();
+        this.version           = version;
+        outerElements          = new ArrayList<>();
+        renamedAttributes      = new ArrayList<>();
+        outerElementProperties = Collections.emptyMap();
+    }
+
+    /**
+     * 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;
     }
 
     /**
@@ -188,6 +217,7 @@ abstract class Transformer {
         {
             Map<String,String> attributes = null;               // All 
attributes for a given type.
             String namespace = null;                            // Value to 
store in 'attributes' map.
+            String type = null;                                 // Class or 
code list containing attributes.
             String line;
             while ((line = in.readLine()) != null) {
                 final int length = line.length();
@@ -199,10 +229,12 @@ abstract class Transformer {
                             if (!isNamespace(element)) break;                  
     // Report illegal format.
                             namespace  = element.intern();
                             attributes = null;
+                            type       = null;
                             continue;
                         }
                         case 1: {                                              
     // New type in above namespace URI.
-                            attributes = m.computeIfAbsent(element.intern(), 
(k) -> new HashMap<>());
+                            type = element.intern();
+                            attributes = m.computeIfAbsent(type, (k) -> new 
HashMap<>());
                             continue;
                         }
                         case 2: {                                              
     // New attribute in above type.
@@ -213,7 +245,7 @@ abstract class Transformer {
                                 element = 
element.substring(s+1).trim().intern();
                                 attributes.put(old, element);
                             } else {
-                                element = element.intern();
+                                element = element.equals(TYPE_KEY) ? type : 
element.intern();
                             }
                             attributes.put(element, namespace);
                             continue;
@@ -235,18 +267,6 @@ abstract class Transformer {
     }
 
     /**
-     * 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() {
@@ -262,9 +282,9 @@ abstract class Transformer {
     }
 
     /**
-     * Returns {@code true} if an element with the given name is likely to be 
an OGC/ISO type (as opposed to property).
-     * For example given the following XML, this method returns {@code true} 
for {@code cit:CI_Date} but {@code false}
-     * for {@code cit:date}:
+     * Returns {@code true} if an element with the given name is an OGC/ISO 
type (as opposed to property).
+     * For example given the following XML, this method returns {@code true} 
for {@code cit:CI_Date} but
+     * {@code false} for {@code cit:date}:
      *
      * {@preformat xml
      *   <cit:CI_Citation>
@@ -277,10 +297,9 @@ abstract class Transformer {
      * }
      *
      * This method is based on simple heuristic applicable to OGC/ISO 
conventions, and may change in any future SIS
-     * version depending on new formats to support. The intent is only to 
reduce the amount of nodes created in the
-     * {@link #outerElements} linked list.
+     * version depending on new formats to support.
      */
-    static boolean isOuterElement(final String localPart) {
+    private static boolean isTypeElement(final String localPart) {
         if (localPart.length() < 4) return false;
         final char c = localPart.charAt(0);
         return (c >= 'A' && c <= 'Z');
@@ -294,9 +313,9 @@ abstract class Transformer {
      */
     final void open(final QName name, final Map<String, Map<String,String>> 
namespaces) {
         final String localPart = name.getLocalPart();
-        if (isOuterElement(localPart)) {
-            outerElements = new OuterElement(outerElements, name,
-                    namespaces.getOrDefault(localPart, 
Collections.emptyMap()));
+        if (isTypeElement(localPart)) {
+            outerElements.add(name);
+            outerElementProperties = namespaces.getOrDefault(localPart, 
Collections.emptyMap());
         }
     }
 
@@ -305,34 +324,70 @@ abstract class Transformer {
      * 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.
      *
-     * @param  name  element name as declared in JAXB annotations.
+     * @param  name        element name as declared in JAXB annotations.
+     * @param  namespaces  namespaces map loaded by {@link #load(String)}.
      */
-    final void close(final QName name) {
-        if (isOuterElement(name.getLocalPart())) {
-            OuterElement e = outerElements;
-            while (e != null) {
-                if (name.equals(e.name)) {
-                    outerElements = e.parent;       // Discard e and all 
children of e.
+    final void close(final QName name, final Map<String, Map<String,String>> 
namespaces) {
+        if (isTypeElement(name.getLocalPart())) {
+            outerElementProperties = null;
+            for (int i=outerElements.size(); --i >= 0;) {
+                if (name.equals(outerElements.get(i))) {
+                    outerElements.remove(i);
+                    final String parent = (--i >= 0) ? 
outerElements.get(i).getLocalPart() : null;
+                    outerElementProperties = namespaces.getOrDefault(parent, 
Collections.emptyMap());
                     break;
                 }
-                e = e.parent;
             }
         }
     }
 
     /**
-     * Attributes expected in the namespace of current element. This method 
may return a different {@code Map}
-     * instance after each call to {@link #open(QName, Map)} or {@link 
#close(QName)}. The returned map shall
-     * not be modified.
+     * Frees any resources associated with this reader.
      */
-    final Map<String,String> attributeNS() {
-        return (outerElements != null) ? outerElements.attributeNS : 
Collections.emptyMap();
+    public void close() throws XMLStreamException {
+        outerElementProperties = null;
+        outerElements.clear();
     }
 
     /**
-     * Frees any resources associated with this reader.
+     * Renames en element using the namespaces map give to the {@code open(…)} 
and {@code close(…)} methods.
+     * When unmarshalling, this imports a name read from the XML document to 
the name to give to JAXB.
+     * When marshalling, this exports a name used in JAXB annotation to the 
name to use in XML document.
+     * The new namespace depends on both the old namespace and the element 
name.
+     * The prefix is computed by {@link #prefixReplacement(String, String)}.
+     *
+     * @param   name   the name of the element or attribute currently being 
read or written.
+     * @return  a name with potentially the namespace and the local part 
replaced.
      */
-    public void close() throws XMLStreamException {
-        outerElements = null;
+    final QName convert(QName name) throws XMLStreamException {
+        String localPart = name.getLocalPart();
+        String namespace = outerElementProperties.get(localPart);
+        if (namespace != null && !isNamespace(namespace)) {
+            localPart = namespace;
+            namespace = outerElementProperties.get(localPart);
+        }
+        /*
+         * 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 (!namespace.equals(oldNS) || 
!localPart.equals(name.getLocalPart())) {
+            name = new QName(namespace, localPart, 
prefixReplacement(name.getPrefix(), namespace));
+        }
+        return name;
     }
+
+    /**
+     * Returns the prefix to use for a name in a new namespace.
+     *
+     * @param  previous   the prefix associated to old namespace.
+     * @param  namespace  the new namespace URI.
+     * @return prefix to use for the new namespace.
+     * @throws XMLStreamException if an error occurred while fetching the 
prefix.
+     */
+    abstract String prefixReplacement(String previous, String namespace) 
throws XMLStreamException;
 }

Modified: 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingReader.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingReader.java?rev=1824672&r1=1824671&r2=1824672&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingReader.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingReader.java
 [UTF-8] Sun Feb 18 15:44:50 2018
@@ -30,6 +30,7 @@ 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.collection.BackingStoreException;
 
 import static javax.xml.stream.XMLStreamConstants.*;
 
@@ -59,12 +60,6 @@ final class TransformingReader extends T
     static final String FILENAME = "NamespaceContent.lst";
 
     /**
-     * A key in {@link #NAMESPACES} sub-map meaning that the value (a 
namespace URI) is for the type instead
-     * than for an attribute. Shall be the same string than the one used in 
{@value #FILENAME} resource file.
-     */
-    static final String TYPE_KEY = "<type>";
-
-    /**
      * The mapping from (<var>type</var>, <var>attribute</var>) pairs to 
namespaces.
      *
      * <ul>
@@ -92,7 +87,7 @@ final class TransformingReader extends T
      */
     static String namespace(final String type) {
         final Map<String,String> attributes = NAMESPACES.get(type);
-        return (attributes != null) ? attributes.get(TYPE_KEY) : null;
+        return (attributes != null) ? attributes.get(type) : null;
     }
 
     /**
@@ -105,6 +100,8 @@ final class TransformingReader extends T
      * computed by {@link Namespaces#getPreferredPrefix(String, String)} or 
any other means. We store
      * the prefix both for performance reasons and for improving the 
guarantees that the URI → prefix
      * mapping is stable.
+     *
+     * @see #prefixReplacement(String, String)
      */
     private final Map<String,String> prefixes;
 
@@ -160,14 +157,11 @@ final class TransformingReader extends T
      */
     @Override
     public Object next() {
-        final XMLEvent event = (XMLEvent) in.next();
-        final XMLEvent next  = nextEvent;
-        if (next != null) {
-            nextEvent = null;
-            assert isWrapper(event, next) : event;
-            return next;
+        try {
+            return nextEvent();
+        } catch (XMLStreamException e) {
+            throw new BackingStoreException(e);
         }
-        return convert(event);
     }
 
     /**
@@ -215,7 +209,7 @@ final class TransformingReader extends T
      * @return the converted event (may be the same instance).
      */
     @SuppressWarnings("unchecked")      // TODO: remove on JDK9
-    private XMLEvent convert(XMLEvent event) {
+    private XMLEvent convert(XMLEvent event) throws XMLStreamException {
         switch (event.getEventType()) {
             case ATTRIBUTE: {
                 event = convert((Attribute) event);
@@ -255,7 +249,7 @@ final class TransformingReader extends T
                 if (namespaces != null) {
                     event = new TransformedEvent.End(e, name, namespaces);
                 }
-                close(originalName);                        // Must be invoked 
only after 'convert(QName)'
+                close(originalName, NAMESPACES);            // Must be invoked 
only after 'convert(QName)'
                 break;
             }
         }
@@ -263,65 +257,10 @@ final class TransformingReader extends T
     }
 
     /**
-     * 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   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 QName convert(final QName name) {
-        String namespace;                               // In this method, 
null means no change to given name.
-        String localPart = name.getLocalPart();
-        /*
-         * 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.
-         */
-        Map<String,String> attributeNS;
-        if (isOuterElement(localPart) && (attributeNS = 
NAMESPACES.get(localPart)) != null) {
-            namespace = attributeNS.get(TYPE_KEY);      // May be null.
-        } else {
-            /*
-             * If the element is not a root element, we need to use the map of 
attributes
-             * of the parent element.
-             */
-            attributeNS = attributeNS();
-            namespace = attributeNS.get(localPart);
-            if (namespace != null && !isNamespace(namespace)) {
-                localPart = namespace;
-                namespace = attributeNS.get(namespace);
-            }
-        }
-        /*
-         * 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 (namespace.equals(oldNS) && localPart.equals(name.getLocalPart())) {
-            return name;
-        }
-        /*
-         * Build a new name if any component (URI or local part) changed. The 
prefix should have
-         * been specified (indirectly) by a previous call to 
'importNS(Namespace)', for example
-         * as a result of a NAMESPACE event. If not, we compute it now using 
the same method.
-         */
-        final String prefix = prefixes.computeIfAbsent(namespace,
-                (ns) -> Namespaces.getPreferredPrefix(ns, name.getPrefix()));
-        return new QName(namespace, localPart, prefix);
-    }
-
-    /**
      * 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) {
+    private Attribute convert(Attribute attribute) throws XMLStreamException {
         final QName originalName = attribute.getName();
         final QName name = convert(originalName);
         if (name != originalName) {
@@ -331,6 +270,20 @@ final class TransformingReader extends T
     }
 
     /**
+     * Returns the prefix to use for a name in a new namespace. The prefix 
should have been specified (indirectly)
+     * by a previous call to {@code importNS(Namespace, …)}, for example as a 
result of a {@code NAMESPACE} event.
+     * If not, we compute it now using the same algorithm than in {@code 
importNS}.
+     *
+     * @param  previous   the prefix associated to old namespace.
+     * @param  namespace  the new namespace URI.
+     * @return prefix to use for the new namespace.
+     */
+    @Override
+    final String prefixReplacement(final String previous, final String 
namespace) {
+        return prefixes.computeIfAbsent(namespace, (ns) -> 
Namespaces.getPreferredPrefix(ns, previous));
+    }
+
+    /**
      * Converts a namespace read from the XML document to the namespace used 
by JAXB annotations.
      * This methods can convert the namespace for which there is a bijective 
mapping, for example
      * {@code "http://www.isotc211.org/2005/gco"} to {@code 
"http://standards.iso.org/iso/19115/-3/gco/1.0"}.
@@ -350,8 +303,7 @@ final class TransformingReader extends T
             uri = removeTrailingSlash(uri);
             final String imported = uri.equals(oldURI) ? newURI : 
version.importNS(uri);
             if (imported != uri) {
-                final String prefix = prefixes.computeIfAbsent(imported,
-                        (ns) -> Namespaces.getPreferredPrefix(ns, 
namespace.getPrefix()));
+                final String prefix = prefixReplacement(namespace.getPrefix(), 
imported);
                 return new TransformedEvent.NS(namespace, prefix, imported);
             }
         }

Modified: 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingWriter.java
URL: 
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingWriter.java?rev=1824672&r1=1824671&r2=1824672&view=diff
==============================================================================
--- 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingWriter.java
 [UTF-8] (original)
+++ 
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/TransformingWriter.java
 [UTF-8] Sun Feb 18 15:44:50 2018
@@ -188,32 +188,45 @@ final class TransformingWriter extends T
             final TransformVersion.Replacement r = version.export(uri);
             if (r != null) {
                 uri = r.namespace;
-                /*
-                 * The wrapped XMLEventWriter maintains a mapping from 
prefixes to namespace URIs.
-                 * Arguments are exported URIs (e.g. from legacy ISO 
19139:2007) and return values
-                 * are prefixes computed by 'Namespaces.getPreferredPrefix(…)' 
or any other means.
-                 * We fetch those prefixes for performance reasons and for 
improving the guarantees
-                 * that the URI → prefix mapping is stable, since JAXB seems 
to require them for
-                 * writing namespaces in XML.
-                 */
-                String prefix = out.getPrefix(uri);
-                if (prefix == null) {
-                    prefix = Namespaces.getPreferredPrefix(uri, 
name.getPrefix());
-                    out.setPrefix(prefix, uri);
-                    /*
-                     * The above call for 'getPreferredPrefix' above is 
required: JAXB seems to need the prefixes
-                     * for recognizing namespaces. The prefix shall be 
computed in the same way than 'exportIfNew'.
-                     * We enter in this block only for the root element, 
before to parse 'xmlns' attributes. For
-                     * all other elements after the root elements, above call 
to 'out.getPrefix(uri)' should succeed.
-                     */
-                }
-                name = new QName(uri, r.exportProperty(name.getLocalPart()), 
prefix);
+                name = new QName(uri, r.exportProperty(name.getLocalPart()), 
prefixReplacement(name.getPrefix(), uri));
             }
         }
         return name;
     }
 
     /**
+     * Returns the prefix to use for a name in a new namespace.
+     *
+     * @param  previous   the prefix associated to old namespace.
+     * @param  namespace  the new namespace URI.
+     * @return prefix to use for the new namespace.
+     * @throws XMLStreamException if an error occurred while fetching the 
prefix.
+     */
+    @Override
+    final String prefixReplacement(final String previous, final String 
namespace) throws XMLStreamException {
+        /*
+         * The wrapped XMLEventWriter maintains a mapping from prefixes to 
namespace URIs.
+         * Arguments are exported URIs (e.g. from legacy ISO 19139:2007) and 
return values
+         * are prefixes computed by 'Namespaces.getPreferredPrefix(…)' or any 
other means.
+         * We fetch those prefixes for performance reasons and for improving 
the guarantees
+         * that the URI → prefix mapping is stable, since JAXB seems to 
require them for
+         * writing namespaces in XML.
+         */
+        String prefix = out.getPrefix(namespace);
+        if (prefix == null) {
+            prefix = Namespaces.getPreferredPrefix(namespace, previous);
+            out.setPrefix(prefix, namespace);
+            /*
+             * The above call for 'getPreferredPrefix' above is required: JAXB 
seems to need the prefixes
+             * for recognizing namespaces. The prefix shall be computed in the 
same way than 'exportIfNew'.
+             * We enter in this block only for the root element, before to 
parse 'xmlns' attributes. For
+             * all other elements after the root elements, above call to 
'out.getPrefix(uri)' should succeed.
+             */
+        }
+        return prefix;
+    }
+
+    /**
      * Returns the attribute to write in the XML document.
      * If there is no name change, then this method returns the given instance 
as-is.
      */


Reply via email to