Author: desruisseaux
Date: Fri Oct 13 12:00:36 2017
New Revision: 1812117
URL: http://svn.apache.org/viewvc?rev=1812117&view=rev
Log:
Parse CompoundCRS also in HTTP URL in addition of URN.
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/DefinitionURITest.java
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java?rev=1812117&r1=1812116&r2=1812117&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/DefinitionURI.java
[UTF-8] Fri Oct 13 12:00:36 2017
@@ -17,10 +17,11 @@
package org.apache.sis.internal.util;
import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
+import java.util.TreeMap;
import java.util.Collections;
import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.internal.system.Loggers;
import static org.apache.sis.util.CharSequences.*;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -135,6 +136,34 @@ public final class DefinitionURI {
private static final char COMPONENT_SEPARATOR = ',';
/**
+ * The separator between a URL and its first {@linkplain #components}.
+ * In URL syntax, this is the separator between URL path and the query.
+ *
+ * <div class="note"><b>Example:</b><code>
+ * http://www.opengis.net/def/crs-compound<u>?</u>1=…&2=…
+ * </code></div>
+ */
+ private static final char COMPONENT_SEPARATOR_1 = '?';
+
+ /**
+ * The separator between {@linkplain #components} in a URL after the first
component.
+ *
+ * <div class="note"><b>Example:</b><code>
+ * http://www.opengis.net/def/crs-compound?1=…<u>&</u>2=…
+ * </code></div>
+ */
+ private static final char COMPONENT_SEPARATOR_2 = '&';
+
+ /**
+ * Separator between keys and values in the query part of a URL.
+ *
+ * <div class="note"><b>Example:</b><code>
+ * http://www.opengis.net/def/crs-compound?1<u>=</u>…&2<u>=</u>…
+ * </code></div>
+ */
+ private static final char KEY_VALUE_SEPARATOR = '=';
+
+ /**
* The domain of URLs in the OGC namespace.
*/
public static final String DOMAIN = "www.opengis.net";
@@ -226,12 +255,12 @@ public final class DefinitionURI {
* </ol></div>
*
* <div class="note"><b>HTTP example:</b> if the URI is
- * {@code
"http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG:9.1:27700&2=(…)/crs/EPSG:9.1:5701"},
+ * {@code
"http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG/9.1/27700&2=(…)/crs/EPSG/9.1/5701"},
* then this {@code DefinitionURI} will contain the {@code
"http://www.opengis.net/def/crs-compound"}
* header with two components:
* <ol>
- * <li>{@code
"http://http://www.opengis.net/def/crs/EPSG:9.1:27700"}</li>
- * <li>{@code "http://http://www.opengis.net/def/crs/EPSG:9.1:5701"}</li>
+ * <li>{@code
"http://http://www.opengis.net/def/crs/EPSG/9.1/27700"}</li>
+ * <li>{@code "http://http://www.opengis.net/def/crs/EPSG/9.1/5701"}</li>
* </ol></div>
*
* Note that this array may contain {@code null} elements if we failed to
parse the corresponding component.
@@ -254,7 +283,7 @@ public final class DefinitionURI {
*/
public static DefinitionURI parse(final String uri) {
ensureNonNull("uri", uri);
- return parse(uri, false, -1, uri.length(), SEPARATOR,
COMPONENT_SEPARATOR);
+ return parse(uri, false, -1, uri.length());
}
/**
@@ -263,19 +292,17 @@ public final class DefinitionURI {
*
* This method may invoke itself recursively if the URI contains
sub-components.
*
- * @param uri the URI to parse.
- * @param prefixIsOptional {@code true} if {@value #PREFIX} may not be
present.
- * @param upper upper index of the previous URI part, or -1
if none.
- * @param stopAt index (exclusive) where to stop parsing.
- * @param separator separator character of URI parts.
- * @param componentSeparator separator character of {@linkplain
#components}.
+ * @param uri the URI to parse.
+ * @param prefixIsOptional {@code true} if {@value #PREFIX} may not be
present.
+ * @param upper upper index of the previous URI part, or -1
if none.
+ * @param stopAt index (exclusive) where to stop parsing.
* @return the parse result, or {@code null} if the URI is not recognized.
*/
@SuppressWarnings("fallthrough")
- private static DefinitionURI parse(final String uri, boolean
prefixIsOptional,
- int upper, int stopAt, char separator, char componentSeparator)
- {
- DefinitionURI result = null;
+ private static DefinitionURI parse(final String uri, boolean
prefixIsOptional, int upper, int stopAt) {
+ DefinitionURI result = null;
+ char separator = SEPARATOR; // Separator
character of URI parts.
+ char componentSeparator = COMPONENT_SEPARATOR; // Separator
character of components.
/*
* Loop on all parts that we expect in the URI. Those parts are:
*
@@ -311,11 +338,11 @@ public final class DefinitionURI {
return result;
}
if (!uri.regionMatches(upper, "//", 0, 2)) {
- return null; // Prefix is never
optional for HTTP.
+ return null; //
Prefix is never optional for HTTP.
}
upper++;
- separator = '/'; // Separator for the
HTTP namespace.
- componentSeparator = '?'; // Separator for the
query part in URL.
+ separator = '/'; //
Separator for the HTTP namespace.
+ componentSeparator = COMPONENT_SEPARATOR_1; //
Separator for the query part in URL.
prefixIsOptional = false;
break;
} else if (regionMatches("urn", uri, lower, upper)) {
@@ -364,23 +391,41 @@ public final class DefinitionURI {
if (stopAt < upper) {
upper = stopAt;
}
- if (componentSeparator == '?') {
- componentSeparator = '&'; // E.g.
http://(…)/crs-compound?1=(…)&2=(…)
+ if (componentSeparator == COMPONENT_SEPARATOR_1) {
+ componentSeparator = COMPONENT_SEPARATOR_2; //
E.g. http://(…)/crs-compound?1=(…)&2=(…)
}
if (result == null) {
result = new DefinitionURI();
}
final boolean isURN = !result.isHTTP;
- final List<DefinitionURI> cmp = new ArrayList<>(4);
+ final Map<Integer,DefinitionURI> orderedComponents =
new TreeMap<>();
boolean hasMore;
do {
+ /*
+ * Find indices of URI sub-component to parse. The
sub-component will
+ * go from 'splitAt' to 'next' exclusive
('splitAt' is exclusive too).
+ */
int next = uri.indexOf(componentSeparator,
splitAt+1);
hasMore = next >= 0 && next < componentsEnd;
if (!hasMore) next = componentsEnd;
- cmp.add(parse(uri, isURN, splitAt, next,
separator, componentSeparator));
+ /*
+ * HTTP uses key-value pairs as in
"http://something?1=...&2=...
+ * URN uses a comma-separated value list without
number.
+ * We support both forms, regardless if HTTP or
URN.
+ */
+ int sequenceNumber = orderedComponents.size() + 1;
// Default value if no explicit key.
+ final int s = splitKeyValue(uri, splitAt+1, next);
+ if (s >= 0) try {
+ sequenceNumber =
Integer.parseInt(trimWhitespaces(uri, splitAt+1, s).toString());
+ splitAt = s; // Set only
on success.
+ } catch (NumberFormatException e) {
+ // Ignore. The URN is likely to be invalid,
but we let parse(…) determines that.
+
Logging.recoverableException(Logging.getLogger(Loggers.CRS_FACTORY),
DefinitionURI.class, "parse", e);
+ }
+ orderedComponents.put(sequenceNumber, parse(uri,
isURN, splitAt, next));
splitAt = next;
} while (hasMore);
- result.components = cmp.toArray(new
DefinitionURI[cmp.size()]);
+ result.components =
orderedComponents.values().toArray(new DefinitionURI[orderedComponents.size()]);
}
// Fall through
}
@@ -431,6 +476,23 @@ public final class DefinitionURI {
}
/**
+ * Returns the index of the {@code '='} character in the given sub-string,
provided that all characters
+ * before it are spaces or decimal digits. Returns -1 if the key-value
separator character is not found.
+ * Note that a positive return value does not guarantee that the number is
parsable.
+ */
+ private static int splitKeyValue(final String uri, int lower, final int
upper) {
+ while (lower < upper) {
+ int c = uri.codePointAt(lower);
+ if ((c < '0' || c > '9') && !Character.isWhitespace(c)) {
+ if (c == KEY_VALUE_SEPARATOR) return lower;
+ break;
+ }
+ lower += Character.charCount(c);
+ }
+ return -1;
+ }
+
+ /**
* Returns the substring of the given URN, ignoring whitespaces and
version number if present.
* The substring is expected to contains at most one {@code ':'}
character. If such separator
* character is present, then that character and everything before it are
ignored. The ignored
@@ -726,16 +788,24 @@ loop: for (int p=0; ; p++) {
* of "urn:ogc:def:crs:::"). This happen with URN defining compound
CRS for instance.
*/
int length = buffer.length();
- while (--length >= 0 && buffer.charAt(length) == separator) {
+ n = isHTTP ? 2 : 1;
+ while ((length -= n) >= 0 && buffer.charAt(length) == separator) {
+ if (isHTTP && buffer.charAt(length + 1) != NO_VERSION.charAt(0))
break;
buffer.setLength(length);
}
/*
* If there is components, format them recursively. Note that the
format is different depending if
* we are formatting URN or HTTP. Example:
"urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701"
- * and
"http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG:9.1:27700&2=(…)/crs/EPSG:9.1:5701".
+ * and
"http://www.opengis.net/def/crs-compound?1=(…)/crs/EPSG/9.1/27700&2=(…)/crs/EPSG/9.1/5701".
*/
if (components != null) {
- for (final DefinitionURI c : components) {
+ for (int i=0; i<components.length;) {
+ final DefinitionURI c = components[i++];
+ if (isHTTP) {
+ buffer.append(i == 1 ? COMPONENT_SEPARATOR_1
+ : COMPONENT_SEPARATOR_2)
+ .append(i).append(KEY_VALUE_SEPARATOR);
+ }
c.toString(buffer, COMPONENT_SEPARATOR);
}
}
Modified:
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/DefinitionURITest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/DefinitionURITest.java?rev=1812117&r1=1812116&r2=1812117&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/DefinitionURITest.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/DefinitionURITest.java
[UTF-8] Fri Oct 13 12:00:36 2017
@@ -147,13 +147,44 @@ public final strictfp class DefinitionUR
* Example: {@code "urn:ogc:def:crs,crs:EPSG:6.3:27700,crs:EPSG:6.3:5701"}.
*/
@Test
+ @DependsOnMethod("testParse")
public void testCompoundURN() {
- final DefinitionURI parsed = DefinitionURI.parse("urn:ogc:def:crs, crs
:EPSG:9.1:27700, crs:EPSG: 9.1 :5701");
- assertNotNull("components", parsed.components);
- assertEquals("components.length", 2, parsed.components.length);
+ DefinitionURI parsed = DefinitionURI.parse("urn:ogc:def:crs, crs
:EPSG:9.1:27700, crs:EPSG: 9.1 :5701");
+ assertNotNull("components", parsed.components);
+ assertEquals("components.length", 2,
parsed.components.length);
assertEquals("urn:ogc:def:crs:EPSG:9.1:27700",
parsed.components[0].toString());
- assertEquals("urn:ogc:def:crs:EPSG:9.1:5701",
parsed.components[1].toString());
+ assertEquals("urn:ogc:def:crs:EPSG:9.1:5701",
parsed.components[1].toString());
assertEquals("urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701",
parsed.toString());
+ /*
+ * The following URN is malformed, but Apache SIS should be tolerant
to some errors.
+ *
+ * - the "urn:ogc:def" prefix should be omitted in components, but
SIS should be able to skip them.
+ * - the "ogc:crs" parts should not be accepted because "def" is
missing between "ogc" and "crs".
+ */
+ parsed =
DefinitionURI.parse("urn:ogc:def:crs,urn:ogc:def:crs:EPSG:9.1:27700,ogc:crs:EPSG:9.1:5701,def:crs:OGC::AnsiDate");
+ assertNotNull("components", parsed.components);
+ assertEquals("components.length", 3,
parsed.components.length);
+ assertEquals("urn:ogc:def:crs:EPSG:9.1:27700",
parsed.components[0].toString());
+ assertNull ("urn:ogc:def:crs:EPSG:9.1:5701", parsed.components[1]);
+ assertEquals("urn:ogc:def:crs:OGC::AnsiDate",
parsed.components[2].toString());
+ }
+
+ /**
+ * Tests compound CRS in HTTP URL.
+ */
+ @Test
+ @DependsOnMethod("testParseHTTP")
+ public void testCompoundHTTP() {
+ DefinitionURI parsed =
DefinitionURI.parse("http://www.opengis.net/def/crs-compound?"
+ + "1=http://www.opengis.net/def/crs/EPSG/9.1/27700&"
+ + "2=http://www.opengis.net/def/crs/EPSG/9.1/5701");
+ assertEquals("type", "crs-compound", parsed.type);
+ assertEquals("components.length", 2, parsed.components.length);
+ assertEquals("http://www.opengis.net/def/crs/EPSG/9.1/27700",
parsed.components[0].toString());
+ assertEquals("http://www.opengis.net/def/crs/EPSG/9.1/5701",
parsed.components[1].toString());
+ assertEquals("http://www.opengis.net/def/crs-compound?"
+ + "1=http://www.opengis.net/def/crs/EPSG/9.1/27700&"
+ + "2=http://www.opengis.net/def/crs/EPSG/9.1/5701",
parsed.toString());
}
/**