private static DefinitionURI parse()

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;
    }