sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1829081 - in /sis/branches/JDK8/storage: sis-storage/src/main/java/org/apache/sis/internal/storage/xml/ sis-storage/src/test/java/org/apache/sis/internal/storage/xml/ sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/ sis-xmls...
Date Fri, 13 Apr 2018 14:53:18 GMT
Author: desruisseaux
Date: Fri Apr 13 14:53:18 2018
New Revision: 1829081

URL: http://svn.apache.org/viewvc?rev=1829081&view=rev
Log:
Allow MIME type detection on XML file without namespace.

Modified:
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/package-info.java
    sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/MimeTypeDetectorTest.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/StoreProvider.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStoreProvider.java

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -35,7 +35,7 @@ import org.apache.sis.internal.storage.D
  * (JAXB, StAX, <i>etc</i>).
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.8
  * @module
  */
@@ -58,28 +58,39 @@ public abstract class AbstractProvider e
     private static final byte[] HEADER = {'<','?','x','m','l',' '};
 
     /**
-     * The mapping from XML namespace to MIME type. This map shall be populated by subclasses
+     * The mapping from XML namespaces to MIME types. This map shall be populated by subclasses
      * at construction time, then never modified anymore since we do not synchronize it.
      *
      * <div class="note"><b>Example</b>
      * public MyDataStore() {
-     *     types.put("http://www.opengis.net/gml/3.2",        "application/gml+xml");
-     *     types.put("http://www.isotc211.org/2005/gmd",      "application/vnd.iso.19139+xml");
-     *     types.put("http://www.opengis.net/cat/csw/2.0.2",  "application/vnd.ogc.csw_xml");
+     *     mimeForNameSpaces.put("http://www.opengis.net/gml/3.2",        "application/gml+xml");
+     *     mimeForNameSpaces.put("http://www.isotc211.org/2005/gmd",      "application/vnd.iso.19139+xml");
+     *     mimeForNameSpaces.put("http://www.opengis.net/cat/csw/2.0.2",  "application/vnd.ogc.csw_xml");
      * }</div>
      */
-    protected final Map<String,String> types;
+    protected final Map<String,String> mimeForNameSpaces;
 
     /**
-     * Creates a new provider. Subclasses shall populate the {@link #types} map with a mapping
+     * The mapping from root elements to MIME types. Used only if the root element is in
+     * the default namespace and contains no {@code xmlns} attributes for that namespace.
+     *
+     * <div class="note"><b>Example</b>
+     * public MyDataStore() {
+     *     mimeForRootElements.put("MD_Metadata", "application/vnd.iso.19139+xml");
+     * }</div>
+     */
+    protected final Map<String,String> mimeForRootElements;
+
+    /**
+     * Creates a new provider. Subclasses shall populate the {@link #mimeForNameSpaces} map
with a mapping
      * from their namespace to the MIME type to declare.
      *
      * @param  name  the primary key to use for searching in the {@code MD_Format} table,
or {@code null} if none.
-     * @param  initialCapacity  initial capacity of the hash map to create.
      */
-    protected AbstractProvider(final String name, final int initialCapacity) {
+    protected AbstractProvider(final String name) {
         super(name);
-        types = new HashMap<>(initialCapacity);
+        mimeForNameSpaces   = new HashMap<>();
+        mimeForRootElements = new HashMap<>();
     }
 
     /**
@@ -109,7 +120,7 @@ public abstract class AbstractProvider e
             }
             // Now check for a more accurate MIME type.
             buffer.position(HEADER.length);
-            final ProbeResult result = new MimeTypeDetector(types) {
+            final ProbeResult result = new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements)
{
                 @Override int read() {
                     if (buffer.hasRemaining()) {
                         return buffer.get();
@@ -136,7 +147,7 @@ public abstract class AbstractProvider e
                 }
             }
             // Now check for a more accurate MIME type.
-            final ProbeResult result = new MimeTypeDetector(types) {
+            final ProbeResult result = new MimeTypeDetector(mimeForNameSpaces, mimeForRootElements)
{
                 private int remaining = READ_AHEAD_LIMIT;
                 @Override int read() throws IOException {
                     return (--remaining >= 0) ? IOUtilities.readCodePoint(reader) : -1;

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -19,6 +19,7 @@ package org.apache.sis.internal.storage.
 import java.util.Map;
 import java.util.Arrays;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.ProbeResult;
 
@@ -35,16 +36,22 @@ import org.apache.sis.storage.ProbeResul
  * it would be way too heavy.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
 abstract class MimeTypeDetector {
     /**
-     * The mapping from XML namespace to MIME type.
+     * The mapping from XML namespaces to MIME types.
      * This map shall be read-only, since we do not synchronize it.
      */
-    private final Map<String,String> types;
+    private final Map<String,String> mimeForNameSpaces;
+
+    /**
+     * The mapping from root elements to MIME types. Used only if the root element is in
+     * the default namespace and contains no {@code xmlns} attributes for that namespace.
+     */
+    private final Map<String,String> mimeForRootElements;
 
     /**
      * The {@code "xmlns"} string as a sequence of bytes.
@@ -79,17 +86,26 @@ abstract class MimeTypeDetector {
     /**
      * Creates a new instance.
      *
-     * @param  types  the mapping from XML namespaces to MIME type.
+     * @param  mimeForNameSpaces    the mapping from XML namespaces to MIME type.
+     * @param  mimeForRootElements  the mapping from root elements to MIME types, used only
as a fallback.
      */
-    MimeTypeDetector(final Map<String,String> types) {
-        this.types = types;
+    MimeTypeDetector(final Map<String,String> mimeForNameSpaces, final Map<String,String>
mimeForRootElements) {
+        this.mimeForNameSpaces   = mimeForNameSpaces;
+        this.mimeForRootElements = mimeForRootElements;
+    }
+
+    /**
+     * Returns the current {@link #buffer} content as a US-ASCII string.
+     */
+    private String current() throws UnsupportedEncodingException {
+        return new String(buffer, 0, length, "US-ASCII");
     }
 
     /**
      * Adds the given byte in the {@link #buffer}, increasing its capacity if needed.
      */
     private void remember(final int c) {
-        if (length == buffer.length) {
+        if (length >= buffer.length) {
             buffer = Arrays.copyOf(buffer, length*2);
         }
         buffer[length++] = (byte) c;
@@ -100,7 +116,7 @@ abstract class MimeTypeDetector {
      * to read. We are typically not allowed to read the full stream because only a limited
amount of bytes is
      * cached. This method may return a Unicode code point (i.e. the returned value may not
fit in {@code char}).
      *
-     * @return the character, or -1 on EOF.
+     * @return the character, or -1 on end of stream window.
      * @throws IOException if an error occurred while reading the byte or character.
      */
     abstract int read() throws IOException;
@@ -110,7 +126,7 @@ abstract class MimeTypeDetector {
      * Characters inside quotes will be ignored.
      *
      * @param  search  the byte or character to skip.
-     * @return the byte or character after {@code search}, or -1 on EOF.
+     * @return the byte or character after {@code search}, or -1 on end of stream window.
      * @throws IOException if an error occurred while reading the bytes or characters.
      */
     private int readAfter(final int search) throws IOException {
@@ -127,14 +143,14 @@ abstract class MimeTypeDetector {
     }
 
     /**
-     * If the given character is a space, skip it and all following spaces.
+     * If the given character is a space, skips it and all following spaces.
      * Returns the first non-space character.
      *
      * <p>For the purpose of this method, a "space" is considered to be the {@code
' '} character
      * and all control characters (character below 32, which include tabulations and line
feeds).
      * This is the same criterion than {@link String#trim()}, but does not include Unicode
spaces.</p>
      *
-     * @return the first non-space character, or -1 on EOF.
+     * @return the first non-space character, or -1 on end of stream window.
      * @throws IOException if an error occurred while reading the bytes or characters.
      */
     private int afterSpaces(int c) throws IOException {
@@ -153,7 +169,7 @@ abstract class MimeTypeDetector {
      * @param  n          number of valid characters in {@code word}.
      * @param  c          value of {@code afterSpaces(read())}.
      * @param  separator  the {@code ':'} or {@code '='} character.
-     * @return 1 if a match is found, 0 if no match, or -1 on EOF.
+     * @return 1 if a match is found, 0 if no match, or -1 on end of stream window.
      * @throws IOException if an error occurred while reading the bytes or characters.
      */
     private int matches(final byte[] word, final int n, int c, final char separator) throws
IOException {
@@ -174,6 +190,7 @@ abstract class MimeTypeDetector {
 
     /**
      * Returns the MIME type, or {@code null} if unknown.
+     * The call shall have already skipped the {@code "<?xml "} characters before to invoke
this method.
      *
      * @throws IOException if an error occurred while reading the bytes or characters.
      */
@@ -221,34 +238,42 @@ abstract class MimeTypeDetector {
          * find the ':' character, then we will consider that the element is in the default
namespace, in which
          * case there is no prefix (length == 0). Exemple: "<MD_Metadata xmlns = … >"
          */
+        final String rootElement;
         c = afterSpaces(c);
         if (c != ':') {
-            length = 0;         // XML element in the default namespace: it has no prefix.
+            rootElement = current();
+            length = 0;                             // XML element in the default namespace:
it has no prefix.
         } else {
+            rootElement = null;                     // Current buffer content is the prefix,
not the name.
             c = afterSpaces(read());
         }
         /*
          * Search for "xmlns" keyword, ignoring anything before it. If we find a prefix in
the previous step,
          * we will require that "xmlns" is followed by ":prefix" where "prefix" is the prefix
that we found.
          */
-        while (true) {
+        for (;;) {
             int m = matches(XMLNS, XMLNS.length, c, (length == 0) ? '=' : ':');
             if (m != 0) {
                 if (m < 0) {
-                    return null;                            // End of file.
+                    return null;                                        // End of stream
window.
                 }
-                if (length == 0) break;                     // Found match for default namespace.
+                if (length == 0) break;                                 // Found match for
default namespace.
                 m = matches(buffer, length, afterSpaces(read()), '=');
                 if (m != 0) {
                     if (m < 0) {
-                        return null;                        // End of file.
+                        return null;                                    // End of stream
window.
                     }
-                    break;                                  // Found match for prefix.
+                    break;                                              // Found match for
prefix.
                 }
             }
             // Skip everything up to the next space, and check again.
             c = afterSpaces(read());
-            if (c < 0) return null;
+            if (c < 0 || c == '>') {
+                if (c >= 0 && rootElement != null) {
+                    return mimeForRootElements.get(rootElement);
+                }
+                return null;                                // End of stream window or end
of start element.
+            }
         }
         /*
          * At this point, we found the "xmlns" attribute for the prefix of the root element.
@@ -270,7 +295,7 @@ abstract class MimeTypeDetector {
         /*
          * Done reading the "xmlns" attribute value.
          */
-        return types.get(new String(buffer, 0, length, "US-ASCII"));
+        return mimeForNameSpaces.get(current());
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -20,6 +20,7 @@ import org.apache.sis.xml.Namespaces;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 import org.apache.sis.internal.storage.StoreMetadata;
 import org.apache.sis.internal.storage.Capability;
 
@@ -45,11 +46,12 @@ public final class StoreProvider extends
      * Creates a new provider.
      */
     public StoreProvider() {
-        super(null, 8);
-        types.put(Namespaces.GML, "application/gml+xml");
-        types.put(Namespaces.GMD, "application/vnd.iso.19139+xml");
-        types.put(Namespaces.CSW, "application/vnd.ogc.csw_xml");
+        super(null);
+        mimeForNameSpaces.put(Namespaces.GML, "application/gml+xml");
+        mimeForNameSpaces.put(Namespaces.CSW, "application/vnd.ogc.csw_xml");
+        mimeForNameSpaces.put(LegacyNamespaces.GMD, "application/vnd.iso.19139+xml");
         // More types to be added in future versions.
+        mimeForRootElements.put("MD_Metadata", "application/vnd.iso.19139+xml");
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/package-info.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/package-info.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/package-info.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -26,7 +26,7 @@
  * the {@code sis-xmlstore} module extends this package with classes designed for use with
StAX cursor API.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */

Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/MimeTypeDetectorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/MimeTypeDetectorTest.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/MimeTypeDetectorTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/xml/MimeTypeDetectorTest.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -16,15 +16,17 @@
  */
 package org.apache.sis.internal.storage.xml;
 
+import java.util.Map;
+import java.util.Collections;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringReader;
 import org.apache.sis.xml.Namespaces;
 import org.apache.sis.metadata.xml.TestUsingFile;
+import org.apache.sis.internal.jaxb.LegacyNamespaces;
 import org.apache.sis.test.DependsOnMethod;
 import org.junit.Test;
 
-import static java.util.Collections.singletonMap;
 import static org.junit.Assert.*;
 import static org.apache.sis.metadata.iso.extent.DefaultExtentTest.FILENAME;
 
@@ -47,7 +49,7 @@ public final strictfp class MimeTypeDete
     public void testInDefaultNamespace() throws IOException {
         testFromString("<?xml version=\"1.0\" standalone=\"yes\"?>\n" +
                        "<MD_Metadata xmlns:xsi=\"" + Namespaces.XSI + "\""  +
-                                       " xmlns=\"" + Namespaces.GMD + "\"/>\n");
+                                       " xmlns=\"" + LegacyNamespaces.GMD + "\"/>\n");
     }
 
     /**
@@ -65,16 +67,25 @@ public final strictfp class MimeTypeDete
      * Implementation of test methods using a hard-coded XML string as a source.
      */
     private static void testFromString(final String xml) throws IOException {
+        assertEquals("application/vnd.iso.19139+xml", getMimeType(xml, Collections.emptyMap()));
+    }
+
+    /**
+     * Returns the MIME type of the given XML, as detected by {@link MimeTypeDetector}.
+     */
+    private static String getMimeType(final String xml, final Map<String,String> mimeForRootElements)
throws IOException {
         final StringReader in = new StringReader(xml);
         assertEquals('<', in.read());
         assertEquals('?', in.read());
-        final MimeTypeDetector detector = new MimeTypeDetector(singletonMap(Namespaces.GMD,
"application/vnd.iso.19139+xml")) {
+        final MimeTypeDetector detector = new MimeTypeDetector(
+                Collections.singletonMap(LegacyNamespaces.GMD, "application/vnd.iso.19139+xml"),
+                mimeForRootElements)
+        {
             @Override int read() throws IOException {
                 return in.read();
             }
         };
-        final String type = detector.getMimeType();
-        assertEquals("application/vnd.iso.19139+xml", type);
+        return detector.getMimeType();
     }
 
     /**
@@ -90,7 +101,10 @@ public final strictfp class MimeTypeDete
         try (InputStream in = TestUsingFile.class.getResourceAsStream(XML2007+FILENAME))
{
             assertEquals('<', in.read());
             assertEquals('?', in.read());
-            final MimeTypeDetector detector = new MimeTypeDetector(singletonMap(Namespaces.GMD,
"application/vnd.iso.19139+xml")) {
+            final MimeTypeDetector detector = new MimeTypeDetector(
+                    Collections.singletonMap(LegacyNamespaces.GMD, "application/vnd.iso.19139+xml"),
+                    Collections.emptyMap())
+            {
                 @Override int read() throws IOException {
                     return in.read();
                 }
@@ -99,4 +113,18 @@ public final strictfp class MimeTypeDete
         }
         assertEquals("application/vnd.iso.19139+xml", type);
     }
+
+    /**
+     * Tests detection for a XML without namespace.
+     *
+     * @throws IOException if an error occurred while reading the bytes or characters.
+     */
+    @Test
+    public void testWithoutNamespace() throws IOException {
+        final String type = getMimeType(
+                "<?xml version=\"1.0\" standalone=\"yes\"?>\n" +
+                "<MD_Metadata xmlns:xsi=\"" + Namespaces.XSI + "\">\n",
+                Collections.singletonMap("MD_Metadata", "application/vnd.iso.19115+xml"));
+        assertEquals("application/vnd.iso.19115+xml", type);
+    }
 }

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/StoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/StoreProvider.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/StoreProvider.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/StoreProvider.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -61,9 +61,10 @@ public final class StoreProvider extends
      * Creates a new GPX store provider.
      */
     public StoreProvider() {
-        super("GPX", 4);
-        types.put(Tags.NAMESPACE_V10, "application/gpx+xml");
-        types.put(Tags.NAMESPACE_V11, "application/gpx+xml");
+        super("GPX");
+        mimeForNameSpaces.put(Tags.NAMESPACE_V10, "application/gpx+xml");
+        mimeForNameSpaces.put(Tags.NAMESPACE_V11, "application/gpx+xml");
+        mimeForRootElements.put("gpx", "application/gpx+xml");
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStoreProvider.java?rev=1829081&r1=1829080&r2=1829081&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStoreProvider.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStoreProvider.java
[UTF-8] Fri Apr 13 14:53:18 2018
@@ -40,14 +40,13 @@ public abstract class StaxDataStoreProvi
     private volatile MarshallerPool jaxb;
 
     /**
-     * Creates a new provider. Subclasses shall populate the {@link #types} map with a mapping
-     * from their namespace to the MIME type to declare.
+     * Creates a new provider. Subclasses shall populate the {@link #mimeForNameSpaces}
+     * map with a mapping from their namespace to the MIME type to declare.
      *
      * @param  name  the primary key to use for searching in the {@code MD_Format} table,
or {@code null} if none.
-     * @param  initialCapacity  initial capacity of the hash map to create.
      */
-    protected StaxDataStoreProvider(final String name, final int initialCapacity) {
-        super(name, initialCapacity);
+    protected StaxDataStoreProvider(final String name) {
+        super(name);
     }
 
     /**



Mime
View raw message