in endorsed/src/org.apache.sis.util/main/org/apache/sis/util/privy/DefinitionURI.java [292:453]
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:
*
* 0: "urn" or "http://www.opengis.net"
* 1: "ogc" or "x-ogc"
* 2: "def"
* 3: "crs", "datum" or other types. The value is not controlled by this method.
* 4: "ogc", "epsg", or other authorities. The value is not controlled by this method.
* 5: version, or null if none.
* 6: code
* 7: parameters, or null if none.
*/
for (int part = 0; part <= 6; part++) {
final int lower = upper + 1;
upper = uri.indexOf(separator, lower);
if (upper < 0 || upper >= stopAt) {
upper = stopAt;
if (lower > upper) {
return result; // Happen if a part is missing.
}
}
switch (part) {
/*
* Verifies that the 3 first parts are "urn:ogc:def:" or "http://www.opengis.net/def/"
* without storing them. In the particular case of second part, we also accept "x-ogc"
* in addition to "ogc" in URN.
*/
case 0: {
if (regionMatches(Constants.HTTP, uri, lower, upper)) {
result = new DefinitionURI();
result.isHTTP = true;
if (codeForGML(null, null, uri, ++upper, result) != null) {
return result;
}
if (!uri.startsWith("//", upper)) {
return null; // Prefix is never optional for HTTP.
}
upper++;
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)) {
prefixIsOptional = false;
break;
} else if (!prefixIsOptional) {
return null;
}
part++;
// Part is not "urn" but its presence was optional. Maybe it is "ogc". Fall through for checking.
}
case 1: {
final boolean isHTTP = (separator != SEPARATOR);
if (regionMatches(isHTTP ? DOMAIN : "ogc", uri, lower, upper) ||
(!isHTTP && regionMatches("x-ogc", uri, lower, upper)))
{
prefixIsOptional = false;
break;
} else if (!prefixIsOptional) {
return null;
}
part++;
// Part is not "ogc" but its presence was optional. Maybe it is "def". Fall through for checking.
}
case 2: {
if (regionMatches("def", uri, lower, upper)) {
prefixIsOptional = false;
break;
} else if (!prefixIsOptional) {
return null;
}
part++;
// Part is not "def" but its presence was optional. Maybe it is "crs". Fall through for checking.
}
/*
* The forth part is the first one that we want to remember; all cases before this one were
* only verification. This case is also the first part where component separator may appear,
* for example as in "urn:ogc:def:crs,crs:EPSG:9.1:27700,crs:EPSG:9.1:5701". We verify here
* if such components exist, and if so we parse them recursively.
*/
case 3: {
int splitAt = uri.indexOf(componentSeparator, lower);
if (splitAt >= 0 && splitAt < stopAt) {
final int componentsEnd = stopAt;
stopAt = splitAt; // Upper limit of the DefinitionURI created in this method call.
if (stopAt < upper) {
upper = stopAt;
}
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 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;
/*
* 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.
* Current version assumes that the URN was identifying a CRS, but future version
* could choose the logger in a more dynamic way.
*/
Logging.recoverableException(getLogger(Loggers.CRS_FACTORY), DefinitionURI.class, "parse", e);
}
orderedComponents.put(sequenceNumber, parse(uri, isURN, splitAt, next));
splitAt = next;
} while (hasMore);
result.components = orderedComponents.values().toArray(DefinitionURI[]::new);
}
// Fall through
}
/*
* For all parts after the first 3 ones, trim whitespaces and store non-empty values.
*/
default: {
final String value = trimWhitespaces(uri, lower, upper).toString();
if (!value.isEmpty() && (part != 5 || !NO_VERSION.equals(value))) {
if (result == null) {
result = new DefinitionURI();
}
switch (part) {
case 3: result.type = value; break;
case 4: result.authority = value; break;
case 5: result.version = value; break;
case 6: result.code = value; break;
default: throw new AssertionError(part);
}
}
}
}
}
/*
* Take every remaining parts as parameters.
*/
if (result != null && ++upper < stopAt) {
result.parameters = (String[]) split(uri.substring(upper), separator);
}
return result;
}