Author: desruisseaux
Date: Sun Apr 15 17:51:32 2018
New Revision: 1829214

URL: http://svn.apache.org/viewvc?rev=1829214&view=rev
Log:
Replace the namespace in "xsi:type" attribute value and automatically add a 
local "xmlns:lan" attribute if needed.
https://issues.apache.org/jira/browse/SIS-399

Modified:
    
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformedEvent.java
    
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
    
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
    
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
    
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
    
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
    
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/lan/FreeTextMarshallingTest.java
    
sis/branches/JDK8/core/sis-metadata/src/test/resources/org/apache/sis/metadata/xml/2007/PositionalAccuracy.xml

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformedEvent.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformedEvent.java?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformedEvent.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformedEvent.java
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -61,8 +61,8 @@ abstract class TransformedEvent<E extend
      * @param  name   the exported name of the attribute or element.
      */
     TransformedEvent(final E event, final QName name) {
-        this.event  = event;
-        this.name   = name;
+        this.event = event;
+        this.name  = name;
     }
 
     @Override public boolean      isStartElement()          {return false;}
@@ -128,14 +128,16 @@ abstract class TransformedEvent<E extend
 
     /**
      * Wrapper over a namespace emitted during the reading or writing of an 
XML document.
-     * This wrapper is used for changing the namespace URI.
+     * This wrapper is used for changing the namespace URI. The wrapped {@link 
#event}
+     * should be a {@link Namespace}, but this class accepts also the {@link 
Attribute}
+     * super-type for allowing the {@link Type} attribute to create synthetic 
namespaces.
      */
-    static final class NS extends TransformedEvent<Namespace> implements 
Namespace {
+    static final class NS extends TransformedEvent<Attribute> implements 
Namespace {
         /** The URI of the namespace. */
         private final String namespaceURI;
 
         /** Wraps the given event with a different prefix and URI. */
-        NS(final Namespace event, final String prefix, final String 
namespaceURI) {
+        NS(final Attribute event, final String prefix, final String 
namespaceURI) {
             super(event, new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, 
prefix, XMLConstants.XMLNS_ATTRIBUTE));
             this.namespaceURI = namespaceURI;
         }
@@ -145,7 +147,7 @@ abstract class TransformedEvent<E extend
         @Override public String    getNamespaceURI()               {return 
namespaceURI;}
         @Override public String    getValue()                      {return 
namespaceURI;}
         @Override public String    getDTDType()                    {return 
event.getDTDType();}
-        @Override public boolean   isSpecified()                   {return 
event.isSpecified();}
+        @Override public boolean   isSpecified()                   {return 
event instanceof Namespace && event.isSpecified();}
         @Override public String    getPrefix()                     {return 
(name != null) ?  name.getLocalPart() : null;}
         @Override public boolean   isDefaultNamespaceDeclaration() {return 
(name != null) && name.getLocalPart().isEmpty();}
         @Override void write(final Appendable out) throws IOException {
@@ -175,23 +177,29 @@ abstract class TransformedEvent<E extend
         @Override public String    getDTDType()      {return 
event.getDTDType();}
         @Override public boolean   isSpecified()     {return 
event.isSpecified();}
         @Override void write(final Appendable out) throws IOException {
-            name(out).append("=\"").append(event.getValue()).append('"');
+            name(out).append("=\"").append(getValue()).append('"');
         }
     }
 
     /**
-     * The attribute for {@code "xsi:type"}.
+     * The {@code "xsi:type"} attribute. Contrarily to other attributes, the 
name is unchanged compared
+     * to the original attribute; instead the value is different. Even in 
unchanged, the {@link QName}
+     * is specified at construction time because it is required by the parent 
class.
      */
-    static final class TypeAttr extends Attr {
+    static final class Type extends Attr {
         /** The attribute value. */
         private final String value;
 
-        /** Wraps the given event with a different name. */
-        TypeAttr(final Attribute event, final QName name, final String value) {
+        /** If the value requires a new prefix to be bound, the namespace 
declaration for it. */
+        Namespace namespace;
+
+        /** Wraps the given event with a different value. */
+        Type(final Attribute event, final QName name, final String value) {
             super(event, name);
             this.value = value;
         }
 
+        /** Returns the {@code "xsi:type"} attribute value. */
         @Override public String getValue() {return value;}
     }
 

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/Transformer.java
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -310,7 +310,7 @@ abstract class Transformer {
     }
 
     /**
-     * Imports or exports an attribute read or write from/to the XML document.
+     * Imports or exports an attribute read or written from/to the XML 
document.
      * If there is no name change, then this method returns the given instance 
as-is.
      * This method performs a special check for the {@code "xsi:type"} 
attribute:
      * its value is parsed as a name and converted.
@@ -320,29 +320,40 @@ abstract class Transformer {
         if ("type".equals(originalName.getLocalPart()) && 
Namespaces.XSI.equals(originalName.getNamespaceURI())) {
             /*
              * In the special case of "xsi:type", do not convert the attribute 
name.
-             * Instead, parse and convert the attribute value.
+             * Instead, parse and convert the attribute value. For example in 
the following:
+             *
+             *    <cit:title xsi:type="lan:PT_FreeText_PropertyType">
+             *
+             * The "lan" prefix needs to be changed to "gmd" if exporting to 
legacy ISO 19139:2007.
              */
             final String value = attribute.getValue();
             if (value != null) {
                 final int s = value.indexOf(':');
                 if (s >= 0) {
-                    String prefix = value.substring(0, s);
-                    String ns = namespaces.get(prefix);
-                    if (ns != null) {
-                        String localPart = value.substring(s+1);
-                        final Map<String,String> renaming = 
renamingMap().get(localPart);
-                        if (renaming != null) {
-                            QName name = new QName(ns, localPart, prefix);
-                            final Map<String,String> currentMap = 
outerElementProperties;
-                            outerElementProperties = renaming;
-                            name = convert(name);
-                            outerElementProperties = currentMap;
+                    String prefix = value.substring(0, s).trim();
+                    String namespace = namespaces.get(prefix);
+                    if (namespace != null) {
+                        String localPart = value.substring(s+1).trim();
+                        QName name = new QName(namespace, localPart, prefix);
+                        final Map<String,String> currentMap = 
outerElementProperties;
+                        outerElementProperties = 
renamingMap().getOrDefault(localPart, Collections.emptyMap());
+                        final boolean changed = (name != (name = 
convert(name)));
+                        outerElementProperties = currentMap;
+                        if (changed) {
                             prefix    = name.getPrefix();
                             localPart = name.getLocalPart();
-                            final String exported = prefix + ':' + localPart;
-                            if (!exported.equals(value)) {
-                                return new 
TransformedEvent.TypeAttr(attribute, originalName, exported);
+                            namespace = name.getNamespaceURI();
+                            TransformedEvent.Type rt = new 
TransformedEvent.Type(
+                                    attribute, originalName, prefix + ':' + 
localPart);
+                            /*
+                             * At this point we got the new value. For example 
"gmd:PT_FreeText_PropertyType" may
+                             * have been replaced by 
"lan:PT_FreeText_PropertyType". However we need to verify if
+                             * the "lan" prefix has been bound to a namespace, 
otherwise the parsing will fail.
+                             */
+                            if (!namespace.equals(namespaces.get(prefix))) {
+                                rt.namespace = new 
TransformedEvent.NS(attribute, prefix, namespace);
                             }
+                            return rt;
                         }
                     }
                 }

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingReader.java
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -222,21 +222,43 @@ final class TransformingReader extends T
             case START_ELEMENT: {
                 final StartElement e = event.asStartElement();
                 final QName originalName = e.getName();
-                open(originalName);                         // Must be invoked 
before 'convert(QName)'.
-                final QName name = convert(originalName);
-                boolean changed = name != originalName;
+                open(originalName);                             // Must be 
invoked before 'convert(QName)'.
+                final QName name  = convert(originalName);      // Name in the 
transformed XML document.
+                boolean changed   = name != originalName;       // Whether the 
name or an attribute changed.
+                Namespace localNS = null;                       // Additional 
namespace required by "xsi:type".
                 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);
+                    if (a != ae) {
+                        changed = true;
+                        if (localNS == null && ae instanceof 
TransformedEvent.Type) {
+                            localNS = ((TransformedEvent.Type) ae).namespace;
+                        }
+                    }
                 }
-                final List<Namespace> namespaces = importNS(e.getNamespaces(),
+                /*
+                 * The list of namespaces is determined by the "xmlns:foo" 
attributes, which are handled in a
+                 * special way. This list is typically non-empty only in the 
root element, but it is legal to
+                 * have namespace declaration in non-root elements as well.
+                 *
+                 * Special case: if this element contains a "xsi:type" 
attribute and if we changed its value
+                 * (for example from "gmd:PT_FreeText_PropertyType" to 
"lan:PT_FreeText_PropertyType"), then
+                 * we may need to add an extra namespace declaration (e.g. for 
the "lan" prefix).
+                 */
+                List<Namespace> namespaces = importNS(e.getNamespaces(),
                         originalName.getNamespaceURI(), 
name.getNamespaceURI(), changed);
                 if (namespaces != null) {
+                    if (localNS != null) {
+                        if (namespaces.isEmpty()) {
+                            namespaces = Collections.singletonList(localNS);
+                        } else {
+                            namespaces.add(localNS);
+                        }
+                    }
                     event = new TransformedEvent.Start(e, name, namespaces, 
attributes(), version);
                 } else {
-                    renamedAttributes.clear();
+                    renamedAttributes.clear();          // Note: above call to 
attributes() also cleared that list.
                 }
                 break;
             }

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/xml/TransformingWriter.java
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -379,7 +379,7 @@ final class TransformingWriter extends T
                 if (namespaces != null) {
                     event = new Event(e, name, namespaces, attributes(), 
version);
                 } else {
-                    renamedAttributes.clear();
+                    renamedAttributes.clear();          // Note: above call to 
attributes() also cleared that list.
                 }
                 /*
                  * At this point, we finished to export the event (i.e. to 
convert namespaces to the URI

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnExport.lst
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -3,6 +3,7 @@
 # Lines with zero-space indentation are namespace URIs.
 # Lines with one-space  indentation are XML types.
 # Lines with two-spaces indentation are properties.
+# actual/exported means that a property needs to be renamed.
 #
 http://www.isotc211.org/2005/gmd
  MD_Georectified

Modified: 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/main/resources/org/apache/sis/xml/RenameOnImport.lst
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -1,7 +1,7 @@
 #
 # Namespaces in which attribute are defined.
 # Lines with zero-space indentation are namespace URIs.
-# Lines with one-space indentation are XML types.
+# Lines with one-space  indentation are XML types.
 # Lines with two-spaces indentation are properties.
 # old/new means that a property needs to be renamed.
 #

Modified: 
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/lan/FreeTextMarshallingTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/lan/FreeTextMarshallingTest.java?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/lan/FreeTextMarshallingTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/lan/FreeTextMarshallingTest.java
 [UTF-8] Sun Apr 15 17:51:32 2018
@@ -20,8 +20,9 @@ import java.util.Locale;
 import javax.xml.bind.JAXBException;
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.util.iso.DefaultInternationalString;
-import org.apache.sis.test.XMLTestCase;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 import org.apache.sis.xml.Namespaces;
+import org.apache.sis.test.XMLTestCase;
 import org.junit.Test;
 
 import static org.apache.sis.test.MetadataAssert.*;
@@ -52,7 +53,42 @@ public final strictfp class FreeTextMars
     }
 
     /**
-     * Tests parsing of a free text in an ISO 19139-compliant way.
+     * Tests parsing of a free text in an ISO 19139 compliant way.
+     * The free text is wrapped inside a citation for marshalling
+     * purpose, but only the free text is actually tested.
+     *
+     * @throws JAXBException if the XML in this test can not be parsed by JAXB.
+     */
+    @Test
+    public void testLegacy() throws JAXBException {
+        final String expected =
+                "<gmd:CI_Citation xmlns:gmd=\"" + LegacyNamespaces.GMD + '"'
+                              + " xmlns:gco=\"" + LegacyNamespaces.GCO + '"'
+                              + " xmlns:xsi=\"" + Namespaces.XSI + "\">\n" +
+                "  <gmd:title xsi:type=\"gmd:PT_FreeText_PropertyType\">\n" +
+                "    <gco:CharacterString>OpenSource 
Project</gco:CharacterString>\n" +
+                "    <gmd:PT_FreeText>\n" +
+                "      <gmd:textGroup>\n" +
+                "        <gmd:LocalisedCharacterString 
locale=\"#locale-eng\">OpenSource Project</gmd:LocalisedCharacterString>\n" +
+                "      </gmd:textGroup>\n" +
+                "      <gmd:textGroup>\n" +
+                "        <gmd:LocalisedCharacterString 
locale=\"#locale-ita\">Progetto OpenSource</gmd:LocalisedCharacterString>\n" +
+                "      </gmd:textGroup>\n" +
+                "      <gmd:textGroup>\n" +
+                "        <gmd:LocalisedCharacterString 
locale=\"#locale-fra\">Projet OpenSource</gmd:LocalisedCharacterString>\n" +
+                "      </gmd:textGroup>\n" +
+                "    </gmd:PT_FreeText>\n" +
+                "  </gmd:title>\n" +
+                "</gmd:CI_Citation>\n";
+
+        final Citation citation = unmarshal(Citation.class, expected);
+        assertEquals(getExpectedI18N(), citation.getTitle());
+        final String actual = marshal(citation, VERSION_2007);
+        assertXmlEquals(expected, actual, "xmlns:*");
+    }
+
+    /**
+     * Tests parsing of a free text in an ISO 19115-3 compliant way.
      * The free text is wrapped inside a citation for marshalling
      * purpose, but only the free text is actually tested.
      *
@@ -88,7 +124,7 @@ public final strictfp class FreeTextMars
     }
 
     /**
-     * Tests parsing of a free text in the legacy (pre-Geotk 3.17) format.
+     * Tests parsing of a free text in a non-standard variant.
      * We continue to support this format for compatibility reason, but
      * also because it is more compact and closer to what we would expect
      * inside a {@code <textGroup>} node.
@@ -96,7 +132,7 @@ public final strictfp class FreeTextMars
      * @throws JAXBException if the XML in this test can not be parsed by JAXB.
      */
     @Test
-    public void testLegacy() throws JAXBException {
+    public void testNonStandard() throws JAXBException {
         final String legacy =
                 "<cit:CI_Citation xmlns:lan=\"" + Namespaces.LAN + '"'
                               + " xmlns:cit=\"" + Namespaces.CIT + '"'

Modified: 
sis/branches/JDK8/core/sis-metadata/src/test/resources/org/apache/sis/metadata/xml/2007/PositionalAccuracy.xml
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/test/resources/org/apache/sis/metadata/xml/2007/PositionalAccuracy.xml?rev=1829214&r1=1829213&r2=1829214&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-metadata/src/test/resources/org/apache/sis/metadata/xml/2007/PositionalAccuracy.xml
 (original)
+++ 
sis/branches/JDK8/core/sis-metadata/src/test/resources/org/apache/sis/metadata/xml/2007/PositionalAccuracy.xml
 Sun Apr 15 17:51:32 2018
@@ -21,7 +21,6 @@
 <gmd:DQ_RelativeInternalPositionalAccuracy
     xmlns:gmd = "http://www.isotc211.org/2005/gmd";
     xmlns:gco = "http://www.isotc211.org/2005/gco";
-    xmlns:lan = "http://standards.iso.org/iso/19115/-3/lan/1.0";
     xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance";
     xsi:schemaLocation = "http://www.isotc211.org/2005/gmd 
http://schemas.opengis.net/iso/19139/20070417/gmd/gmd.xsd";>
 


Reply via email to