151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/*
251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is free software; you can redistribute it and/or modify it
651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * under the terms of the GNU General Public License version 2 only, as
751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * published by the Free Software Foundation.  Oracle designates this
851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * particular file as subject to the "Classpath" exception as provided
951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * by Oracle in the LICENSE file that accompanied this code.
1051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * This code is distributed in the hope that it will be useful, but WITHOUT
1251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * version 2 for more details (a copy is included in the LICENSE file that
1551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * accompanied this code).
1651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
1751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * You should have received a copy of the GNU General Public License version
1851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * 2 along with this work; if not, write to the Free Software Foundation,
1951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski *
2151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * or visit www.oracle.com if you need additional information or have any
2351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * questions.
2451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
2551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskipackage sun.net.www;
2751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
2851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.util.BitSet;
2951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.io.UnsupportedEncodingException;
3051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.io.File;
3151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.net.URL;
3251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.net.MalformedURLException;
3351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.net.URI;
3451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.net.URISyntaxException;
3551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.ByteBuffer;
3651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.CharBuffer;
3751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.charset.CharacterCodingException;
3851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport sun.nio.cs.ThreadLocalCoders;
3951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.charset.CharsetDecoder;
4051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.charset.CoderResult;
4151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskiimport java.nio.charset.CodingErrorAction;
4251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
4351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski/**
4451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * A class that contains useful routines common to sun.net.www
4551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski * @author  Mike McCloskey
4651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski */
4751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
4851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebskipublic class ParseUtil {
4951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static BitSet encodedInPath;
5051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    static {
5251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath = new BitSet(256);
5351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Set the bits corresponding to characters that are encoded in the
5551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // path component of a URI.
5651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
5751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // These characters are reserved in the path segment as described in
5851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // RFC2396 section 3.3.
5951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('=');
6051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set(';');
6151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('?');
6251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('/');
6351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
6451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // These characters are defined as excluded in RFC2396 section 2.4.3
6551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // and must be escaped if they occur in the data part of a URI.
6651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('#');
6751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set(' ');
6851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('<');
6951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('>');
7051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('%');
7151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('"');
7251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('{');
7351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('}');
7451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('|');
7551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('\\');
7651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('^');
7751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('[');
7851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set(']');
7951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set('`');
8051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
8151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // US ASCII control characters 00-1F and 7F.
8251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i=0; i<32; i++)
8351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            encodedInPath.set(i);
8451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        encodedInPath.set(127);
8551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
8651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
8751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
8851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Constructs an encoded version of the specified path string suitable
8951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * for use in the construction of a URL.
9051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     *
9151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * A path separator is replaced by a forward slash. The string is UTF8
9251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * encoded. The % escape sequence is used for characters that are above
9351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * 0x7F or those defined in RFC2396 as reserved or excluded in the path
9451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * component of a URL.
9551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
9651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static String encodePath(String path) {
9751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return encodePath(path, true);
9851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
9951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /*
10051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * flag indicates whether path uses platform dependent
10151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * File.separatorChar or not. True indicates path uses platform
10251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * dependent File.separatorChar.
10351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
10451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static String encodePath(String path, boolean flag) {
10551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        char[] retCC = new char[path.length() * 2 + 16];
10651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int    retLen = 0;
10751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        char[] pathCC = path.toCharArray();
10851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
10951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int n = path.length();
11051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i=0; i<n; i++) {
11151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            char c = pathCC[i];
11251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if ((!flag && c == '/') || (flag && c == File.separatorChar))
11351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                retCC[retLen++] = '/';
11451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            else {
11551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (c <= 0x007F) {
11651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (c >= 'a' && c <= 'z' ||
11751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        c >= 'A' && c <= 'Z' ||
11851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        c >= '0' && c <= '9') {
11951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        retCC[retLen++] = c;
12051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    } else
12151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (encodedInPath.get(c))
12251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        retLen = escape(retCC, c, retLen);
12351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    else
12451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        retCC[retLen++] = c;
12551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else if (c > 0x07FF) {
12651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    retLen = escape(retCC, (char)(0xE0 | ((c >> 12) & 0x0F)), retLen);
12751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    retLen = escape(retCC, (char)(0x80 | ((c >>  6) & 0x3F)), retLen);
12851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);
12951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else {
13051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    retLen = escape(retCC, (char)(0xC0 | ((c >>  6) & 0x1F)), retLen);
13151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);
13251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
13351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
13451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            //worst case scenario for character [0x7ff-] every single
13551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            //character will be encoded into 9 characters.
13651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (retLen + 9 > retCC.length) {
13751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                int newLen = retCC.length * 2 + 16;
13851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (newLen < 0) {
13951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    newLen = Integer.MAX_VALUE;
14051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
14151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                char[] buf = new char[newLen];
14251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                System.arraycopy(retCC, 0, buf, 0, retLen);
14351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                retCC = buf;
14451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
14551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
14651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return new String(retCC, 0, retLen);
14751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
14851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
14951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
15051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Appends the URL escape sequence for the specified char to the
15151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * specified StringBuffer.
15251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
15351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static int escape(char[] cc, char c, int index) {
15451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        cc[index++] = '%';
15551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        cc[index++] = Character.forDigit((c >> 4) & 0xF, 16);
15651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        cc[index++] = Character.forDigit(c & 0xF, 16);
15751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return index;
15851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
15951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
16051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
16151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Un-escape and return the character at position i in string s.
16251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
16351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static byte unescape(String s, int i) {
16451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return (byte) Integer.parseInt(s.substring(i+1,i+3),16);
16551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
16651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
16751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
16851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
16951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns a new String constructed from the specified String by replacing
17051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * the URL escape sequences and UTF8 encoding with the characters they
17151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * represent.
17251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
17351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static String decode(String s) {
17451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int n = s.length();
17551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if ((n == 0) || (s.indexOf('%') < 0))
17651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return s;
17751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
17851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        StringBuilder sb = new StringBuilder(n);
17951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        ByteBuffer bb = ByteBuffer.allocate(n);
18051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CharBuffer cb = CharBuffer.allocate(n);
18151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8")
18251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            .onMalformedInput(CodingErrorAction.REPORT)
18351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            .onUnmappableCharacter(CodingErrorAction.REPORT);
18451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
18551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        char c = s.charAt(0);
18651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = 0; i < n;) {
18751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            assert c == s.charAt(i);
18851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (c != '%') {
18951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(c);
19051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (++i >= n)
19151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    break;
19251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                c = s.charAt(i);
19351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                continue;
19451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
19551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            bb.clear();
19651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            int ui = i;
19751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            for (;;) {
19851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                assert (n - i >= 2);
19951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                try {
20051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    bb.put(unescape(s, i));
20151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } catch (NumberFormatException e) {
20251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    throw new IllegalArgumentException();
20351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
20451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                i += 3;
20551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (i >= n)
20651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    break;
20751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                c = s.charAt(i);
20851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (c != '%')
20951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    break;
21051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
21151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            bb.flip();
21251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            cb.clear();
21351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            dec.reset();
21451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            CoderResult cr = dec.decode(bb, cb, true);
21551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (cr.isError())
21651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                throw new IllegalArgumentException("Error decoding percent encoded characters");
21751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            cr = dec.flush(cb);
21851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (cr.isError())
21951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                throw new IllegalArgumentException("Error decoding percent encoded characters");
22051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append(cb.flip().toString());
22151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
22251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
22351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return sb.toString();
22451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
22551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
22651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    /**
22751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     * Returns a canonical version of the specified string.
22851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski     */
22951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public String canonizeString(String file) {
23051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int i = 0;
23151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int lim = file.length();
23251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
23351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Remove embedded /../
23451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        while ((i = file.indexOf("/../")) >= 0) {
23551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {
23651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                file = file.substring(0, lim) + file.substring(i + 3);
23751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else {
23851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                file = file.substring(i + 3);
23951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
24051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
24151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Remove embedded /./
24251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        while ((i = file.indexOf("/./")) >= 0) {
24351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            file = file.substring(0, i) + file.substring(i + 2);
24451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
24551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Remove trailing ..
24651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        while (file.endsWith("/..")) {
24751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            i = file.indexOf("/..");
24851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {
24951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                file = file.substring(0, lim+1);
25051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else {
25151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                file = file.substring(0, i);
25251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
25351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
25451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // Remove trailing .
25551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (file.endsWith("/."))
25651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            file = file.substring(0, file.length() -1);
25751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
25851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return file;
25951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
26051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
26151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static URL fileToEncodedURL(File file)
26251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        throws MalformedURLException
26351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
26451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String path = file.getAbsolutePath();
26551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        path = ParseUtil.encodePath(path);
26651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (!path.startsWith("/")) {
26751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            path = "/" + path;
26851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
26951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (!path.endsWith("/") && file.isDirectory()) {
27051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            path = path + "/";
27151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
27251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return new URL("file", "", path);
27351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
27451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
27551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    public static java.net.URI toURI(URL url) {
27651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String protocol = url.getProtocol();
27751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String auth = url.getAuthority();
27851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String path = url.getPath();
27951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String query = url.getQuery();
28051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String ref = url.getRef();
28151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (path != null && !(path.startsWith("/")))
28251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            path = "/" + path;
28351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
28451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        //
28551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // In java.net.URI class, a port number of -1 implies the default
28651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        // port number. So get it stripped off before creating URI instance.
28751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        //
28851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (auth != null && auth.endsWith(":-1"))
28951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            auth = auth.substring(0, auth.length() - 3);
29051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
29151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        java.net.URI uri;
29251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
29351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            uri = createURI(protocol, auth, path, query, ref);
29451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } catch (java.net.URISyntaxException e) {
29551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            uri = null;
29651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
29751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return uri;
29851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
29951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
30051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
30151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // createURI() and its auxiliary code are cloned from java.net.URI.
30251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Most of the code are just copy and paste, except that quote()
30351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // has been modified to avoid double-escape.
30451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
30551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Usually it is unacceptable, but we're forced to do it because
30651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // otherwise we need to change public API, namely java.net.URI's
30751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // multi-argument constructors. It turns out that the changes cause
30851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // incompatibilities so can't be done.
30951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
31051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static URI createURI(String scheme,
31151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String authority,
31251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String path,
31351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String query,
31451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String fragment) throws URISyntaxException
31551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
31651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        String s = toString(scheme, null,
31751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            authority, null, null, -1,
31851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            path, query, fragment);
31951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        checkPath(s, scheme, path);
32051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return new URI(s);
32151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
32251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
32351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static String toString(String scheme,
32451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String opaquePart,
32551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String authority,
32651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String userInfo,
32751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String host,
32851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            int port,
32951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String path,
33051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String query,
33151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            String fragment)
33251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
33351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        StringBuffer sb = new StringBuffer();
33451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (scheme != null) {
33551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append(scheme);
33651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append(':');
33751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
33851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        appendSchemeSpecificPart(sb, opaquePart,
33951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 authority, userInfo, host, port,
34051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 path, query);
34151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        appendFragment(sb, fragment);
34251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return sb.toString();
34351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
34451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
34551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void appendSchemeSpecificPart(StringBuffer sb,
34651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String opaquePart,
34751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String authority,
34851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String userInfo,
34951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String host,
35051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          int port,
35151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String path,
35251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                          String query)
35351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
35451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (opaquePart != null) {
35551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            /* check if SSP begins with an IPv6 address
35651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski             * because we must not quote a literal IPv6 address
35751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski             */
35851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (opaquePart.startsWith("//[")) {
35951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                int end =  opaquePart.indexOf("]");
36051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (end != -1 && opaquePart.indexOf(":")!=-1) {
36151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    String doquote, dontquote;
36251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (end == opaquePart.length()) {
36351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        dontquote = opaquePart;
36451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        doquote = "";
36551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    } else {
36651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        dontquote = opaquePart.substring(0,end+1);
36751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        doquote = opaquePart.substring(end+1);
36851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
36951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append (dontquote);
37051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append(quote(doquote, L_URIC, H_URIC));
37151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
37251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else {
37351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(quote(opaquePart, L_URIC, H_URIC));
37451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
37551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } else {
37651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            appendAuthority(sb, authority, userInfo, host, port);
37751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (path != null)
37851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(quote(path, L_PATH, H_PATH));
37951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (query != null) {
38051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append('?');
38151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(quote(query, L_URIC, H_URIC));
38251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
38351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
38451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
38551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
38651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void appendAuthority(StringBuffer sb,
38751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String authority,
38851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String userInfo,
38951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 String host,
39051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                 int port)
39151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
39251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (host != null) {
39351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append("//");
39451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (userInfo != null) {
39551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(quote(userInfo, L_USERINFO, H_USERINFO));
39651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append('@');
39751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
39851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            boolean needBrackets = ((host.indexOf(':') >= 0)
39951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                    && !host.startsWith("[")
40051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                    && !host.endsWith("]"));
40151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (needBrackets) sb.append('[');
40251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append(host);
40351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (needBrackets) sb.append(']');
40451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (port != -1) {
40551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(':');
40651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(port);
40751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
40851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } else if (authority != null) {
40951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append("//");
41051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (authority.startsWith("[")) {
41151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                int end = authority.indexOf("]");
41251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (end != -1 && authority.indexOf(":")!=-1) {
41351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    String doquote, dontquote;
41451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (end == authority.length()) {
41551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        dontquote = authority;
41651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        doquote = "";
41751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    } else {
41851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        dontquote = authority.substring(0,end+1);
41951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        doquote = authority.substring(end+1);
42051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
42151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append (dontquote);
42251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append(quote(doquote,
42351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            L_REG_NAME | L_SERVER,
42451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            H_REG_NAME | H_SERVER));
42551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
42651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else {
42751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append(quote(authority,
42851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            L_REG_NAME | L_SERVER,
42951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                            H_REG_NAME | H_SERVER));
43051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
43151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
43251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
43351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
43451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void appendFragment(StringBuffer sb, String fragment) {
43551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (fragment != null) {
43651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append('#');
43751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            sb.append(quote(fragment, L_URIC, H_URIC));
43851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
43951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
44051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
44151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Quote any characters in s that are not permitted
44251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // by the given mask pair
44351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
44451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static String quote(String s, long lowMask, long highMask) {
44551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int n = s.length();
44651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        StringBuffer sb = null;
44751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
44851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = 0; i < s.length(); i++) {
44951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            char c = s.charAt(i);
45051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (c < '\u0080') {
45151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (!match(c, lowMask, highMask) && !isEscaped(s, i)) {
45251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (sb == null) {
45351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        sb = new StringBuffer();
45451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        sb.append(s.substring(0, i));
45551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    }
45651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    appendEscape(sb, (byte)c);
45751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                } else {
45851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    if (sb != null)
45951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                        sb.append(c);
46051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
46151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else if (allowNonASCII
46251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                       && (Character.isSpaceChar(c)
46351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                           || Character.isISOControl(c))) {
46451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (sb == null) {
46551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb = new StringBuffer();
46651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append(s.substring(0, i));
46751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                }
46851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                appendEncoded(sb, c);
46951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            } else {
47051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                if (sb != null)
47151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                    sb.append(c);
47251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            }
47351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
47451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return (sb == null) ? s : sb.toString();
47551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
47651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
47751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
47851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // To check if the given string has an escaped triplet
47951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // at the given position
48051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
48151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static boolean isEscaped(String s, int pos) {
48251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (s == null || (s.length() <= (pos + 2)))
48351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return false;
48451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
48551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return s.charAt(pos) == '%'
48651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski               && match(s.charAt(pos + 1), L_HEX, H_HEX)
48751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski               && match(s.charAt(pos + 2), L_HEX, H_HEX);
48851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
48951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
49051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void appendEncoded(StringBuffer sb, char c) {
49151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        ByteBuffer bb = null;
49251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        try {
49351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            bb = ThreadLocalCoders.encoderFor("UTF-8")
49451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                .encode(CharBuffer.wrap("" + c));
49551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        } catch (CharacterCodingException x) {
49651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            assert false;
49751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
49851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        while (bb.hasRemaining()) {
49951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            int b = bb.get() & 0xff;
50051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (b >= 0x80)
50151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                appendEscape(sb, (byte)b);
50251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            else
50351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                sb.append((char)b);
50451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
50551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
50651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
50751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private final static char[] hexDigits = {
50851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        '0', '1', '2', '3', '4', '5', '6', '7',
50951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
51051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    };
51151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
51251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void appendEscape(StringBuffer sb, byte b) {
51351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        sb.append('%');
51451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        sb.append(hexDigits[(b >> 4) & 0x0f]);
51551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        sb.append(hexDigits[(b >> 0) & 0x0f]);
51651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
51751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
51851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Tell whether the given character is permitted by the given mask pair
51951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static boolean match(char c, long lowMask, long highMask) {
52051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (c < 64)
52151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return ((1L << c) & lowMask) != 0;
52251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (c < 128)
52351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            return ((1L << (c - 64)) & highMask) != 0;
52451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return false;
52551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
52651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
52751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // If a scheme is given then the path, if given, must be absolute
52851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //
52951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static void checkPath(String s, String scheme, String path)
53051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        throws URISyntaxException
53151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    {
53251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        if (scheme != null) {
53351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if ((path != null)
53451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                && ((path.length() > 0) && (path.charAt(0) != '/')))
53551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                throw new URISyntaxException(s,
53651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                                             "Relative path in absolute URI");
53751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
53851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
53951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
54051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
54151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // -- Character classes for parsing --
54251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
54351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Compute a low-order mask for the characters
54451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // between first and last, inclusive
54551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static long lowMask(char first, char last) {
54651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        long m = 0;
54751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int f = Math.max(Math.min(first, 63), 0);
54851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int l = Math.max(Math.min(last, 63), 0);
54951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = f; i <= l; i++)
55051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            m |= 1L << i;
55151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return m;
55251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
55351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
55451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Compute the low-order mask for the characters in the given string
55551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static long lowMask(String chars) {
55651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int n = chars.length();
55751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        long m = 0;
55851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = 0; i < n; i++) {
55951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            char c = chars.charAt(i);
56051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if (c < 64)
56151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                m |= (1L << c);
56251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
56351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return m;
56451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
56551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
56651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Compute a high-order mask for the characters
56751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // between first and last, inclusive
56851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static long highMask(char first, char last) {
56951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        long m = 0;
57051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int f = Math.max(Math.min(first, 127), 64) - 64;
57151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int l = Math.max(Math.min(last, 127), 64) - 64;
57251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = f; i <= l; i++)
57351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            m |= 1L << i;
57451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return m;
57551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
57651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
57751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Compute the high-order mask for the characters in the given string
57851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static long highMask(String chars) {
57951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        int n = chars.length();
58051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        long m = 0;
58151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        for (int i = 0; i < n; i++) {
58251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            char c = chars.charAt(i);
58351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski            if ((c >= 64) && (c < 128))
58451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski                m |= (1L << (c - 64));
58551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        }
58651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        return m;
58751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    }
58851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
58951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
59051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Character-class masks
59151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
59251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
59351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //            "8" | "9"
59451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_DIGIT = lowMask('0', '9');
59551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_DIGIT = 0L;
59651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
59751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // hex           =  digit | "A" | "B" | "C" | "D" | "E" | "F" |
59851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                          "a" | "b" | "c" | "d" | "e" | "f"
59951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_HEX = L_DIGIT;
60051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_HEX = highMask('A', 'F') | highMask('a', 'f');
60151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
60251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // upalpha  = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
60351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //            "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
60451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //            "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
60551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_UPALPHA = 0L;
60651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_UPALPHA = highMask('A', 'Z');
60751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
60851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
60951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //            "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
61051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //            "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
61151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_LOWALPHA = 0L;
61251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_LOWALPHA = highMask('a', 'z');
61351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
61451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // alpha         = lowalpha | upalpha
61551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
61651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
61751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
61851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // alphanum      = alpha | digit
61951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
62051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
62151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
62251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
62351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                 "(" | ")"
62451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_MARK = lowMask("-_.!~*'()");
62551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_MARK = highMask("-_.!~*'()");
62651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
62751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // unreserved    = alphanum | mark
62851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
62951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
63051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
63151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
63251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                 "$" | "," | "[" | "]"
63351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Added per RFC2732: "[", "]"
63451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_RESERVED = lowMask(";/?:@&=+$,[]");
63551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_RESERVED = highMask(";/?:@&=+$,[]");
63651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
63751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // The zero'th bit is used to indicate that escape pairs and non-US-ASCII
63851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // characters are allowed; this is handled by the scanEscape method below.
63951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_ESCAPED = 1L;
64051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_ESCAPED = 0L;
64151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
64251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // Dash, for use in domainlabel and toplabel
64351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_DASH = lowMask("-");
64451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_DASH = highMask("-");
64551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
64651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // uric          = reserved | unreserved | escaped
64751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_URIC = L_RESERVED | L_UNRESERVED | L_ESCAPED;
64851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_URIC = H_RESERVED | H_UNRESERVED | H_ESCAPED;
64951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
65051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // pchar         = unreserved | escaped |
65151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                 ":" | "@" | "&" | "=" | "+" | "$" | ","
65251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_PCHAR
65351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = L_UNRESERVED | L_ESCAPED | lowMask(":@&=+$,");
65451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_PCHAR
65551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = H_UNRESERVED | H_ESCAPED | highMask(":@&=+$,");
65651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
65751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // All valid path characters
65851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_PATH = L_PCHAR | lowMask(";/");
65951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_PATH = H_PCHAR | highMask(";/");
66051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
66151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // userinfo      = *( unreserved | escaped |
66251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                    ";" | ":" | "&" | "=" | "+" | "$" | "," )
66351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_USERINFO
66451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = L_UNRESERVED | L_ESCAPED | lowMask(";:&=+$,");
66551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_USERINFO
66651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = H_UNRESERVED | H_ESCAPED | highMask(";:&=+$,");
66751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
66851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // reg_name      = 1*( unreserved | escaped | "$" | "," |
66951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    //                     ";" | ":" | "@" | "&" | "=" | "+" )
67051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_REG_NAME
67151b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = L_UNRESERVED | L_ESCAPED | lowMask("$,;:@&=+");
67251b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_REG_NAME
67351b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = H_UNRESERVED | H_ESCAPED | highMask("$,;:@&=+");
67451b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski
67551b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    // All valid characters for server-based authorities
67651b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long L_SERVER
67751b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = L_USERINFO | L_ALPHANUM | L_DASH | lowMask(".:@[]");
67851b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski    private static final long H_SERVER
67951b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski        = H_USERINFO | H_ALPHANUM | H_DASH | highMask(".:@[]");
68051b1b6997fd3f980076b8081f7f1165ccc2a4008Piotr Jastrzebski}
681