001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util;
018
019/**
020 * A Camel specific URI parser that parses endpoint URIs in a quasi syntax that Camel uses.
021 *
022 * The {@link java.net.URI} is much slower and parses endpoint URIs into additional parts which Camel does not use or
023 * need.
024 */
025public final class CamelURIParser {
026
027    private CamelURIParser() {
028    }
029
030    /**
031     * Parses the URI.
032     *
033     * If this parser cannot parse the uri then <tt>null</tt> is returned. And instead the follow code can be used:
034     * 
035     * <pre>
036     * URI u = new URI(UnsafeUriCharactersEncoder.encode(uri, true));
037     * </pre>
038     *
039     * @param  uri the uri
040     *
041     * @return     <tt>null</tt> if not possible to parse, or an array[3] with scheme,path,query
042     */
043    public static String[] parseUri(String uri) {
044        int schemeStart = 0;
045        int schemeEnd = 0;
046        int pathStart = 0;
047        int pathEnd = 0;
048        int queryStart = 0;
049
050        int len = uri.length();
051        for (int i = 0; i < len; i++) {
052            char ch = uri.charAt(i);
053            if (ch > 128) {
054                // must be an ascii char
055                return null;
056            }
057            // must be a safe char
058            if (!UnsafeUriCharactersEncoder.isSafeFastParser(ch)) {
059                return null;
060            }
061            if (schemeEnd == 0) {
062                if (ch == ':') {
063                    schemeEnd = i;
064                    // skip colon
065                    pathStart = i + 1;
066                }
067            } else if (pathEnd == 0) {
068                if (ch == '?') {
069                    pathEnd = i;
070                    // skip ? marker
071                    queryStart = i + 1;
072                }
073            }
074        }
075
076        if (pathStart == 0 && schemeEnd != 0) {
077            // skip colon
078            pathStart = schemeEnd + 1;
079        }
080        // invalid if there is no path anyway
081        if (pathStart >= len) {
082            return null;
083        }
084
085        String scheme = null;
086        if (schemeEnd != 0) {
087            scheme = uri.substring(schemeStart, schemeEnd);
088        }
089        if (scheme == null) {
090            return null;
091        }
092
093        String path;
094        // skip two leading slashes
095        int next = pathStart + 1;
096        if (uri.charAt(pathStart) == '/' && next < len && uri.charAt(next) == '/') {
097            pathStart = pathStart + 2;
098        }
099        if (pathEnd != 0) {
100            path = uri.substring(pathStart, pathEnd);
101        } else {
102            path = uri.substring(pathStart);
103        }
104
105        String query = null;
106        if (queryStart != 0 && queryStart < len) {
107            query = uri.substring(queryStart);
108        }
109
110        return new String[] { scheme, path, query };
111    }
112}