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);
}
/**