680 lines · 24.3 KB · Java
RawDownload
/* * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.www; import java.util.BitSet;import java.io.UnsupportedEncodingException;import java.io.File;import java.net.URL;import java.net.MalformedURLException;import java.net.URI;import java.net.URISyntaxException;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.charset.CharacterCodingException;import sun.nio.cs.ThreadLocalCoders;import java.nio.charset.CharsetDecoder;import java.nio.charset.CoderResult;import java.nio.charset.CodingErrorAction; /** * A class that contains useful routines common to sun.net.www * @author  Mike McCloskey */ public class ParseUtil {    static BitSet encodedInPath;     static {        encodedInPath = new BitSet(256);         // Set the bits corresponding to characters that are encoded in the        // path component of a URI.         // These characters are reserved in the path segment as described in        // RFC2396 section 3.3.        encodedInPath.set('=');        encodedInPath.set(';');        encodedInPath.set('?');        encodedInPath.set('/');         // These characters are defined as excluded in RFC2396 section 2.4.3        // and must be escaped if they occur in the data part of a URI.        encodedInPath.set('#');        encodedInPath.set(' ');        encodedInPath.set('<');        encodedInPath.set('>');        encodedInPath.set('%');        encodedInPath.set('"');        encodedInPath.set('{');        encodedInPath.set('}');        encodedInPath.set('|');        encodedInPath.set('\\');        encodedInPath.set('^');        encodedInPath.set('[');        encodedInPath.set(']');        encodedInPath.set('`');         // US ASCII control characters 00-1F and 7F.        for (int i=0; i<32; i++)            encodedInPath.set(i);        encodedInPath.set(127);    }     /**     * Constructs an encoded version of the specified path string suitable     * for use in the construction of a URL.     *     * A path separator is replaced by a forward slash. The string is UTF8     * encoded. The % escape sequence is used for characters that are above     * 0x7F or those defined in RFC2396 as reserved or excluded in the path     * component of a URL.     */    public static String encodePath(String path) {        return encodePath(path, true);    }    /*     * flag indicates whether path uses platform dependent     * File.separatorChar or not. True indicates path uses platform     * dependent File.separatorChar.     */    public static String encodePath(String path, boolean flag) {        char[] retCC = new char[path.length() * 2 + 16];        int    retLen = 0;        char[] pathCC = path.toCharArray();         int n = path.length();        for (int i=0; i<n; i++) {            char c = pathCC[i];            if ((!flag && c == '/') || (flag && c == File.separatorChar))                retCC[retLen++] = '/';            else {                if (c <= 0x007F) {                    if (c >= 'a' && c <= 'z' ||                        c >= 'A' && c <= 'Z' ||                        c >= '0' && c <= '9') {                        retCC[retLen++] = c;                    } else                    if (encodedInPath.get(c))                        retLen = escape(retCC, c, retLen);                    else                        retCC[retLen++] = c;                } else if (c > 0x07FF) {                    retLen = escape(retCC, (char)(0xE0 | ((c >> 12) & 0x0F)), retLen);                    retLen = escape(retCC, (char)(0x80 | ((c >>  6) & 0x3F)), retLen);                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);                } else {                    retLen = escape(retCC, (char)(0xC0 | ((c >>  6) & 0x1F)), retLen);                    retLen = escape(retCC, (char)(0x80 | ((c >>  0) & 0x3F)), retLen);                }            }            //worst case scenario for character [0x7ff-] every single            //character will be encoded into 9 characters.            if (retLen + 9 > retCC.length) {                int newLen = retCC.length * 2 + 16;                if (newLen < 0) {                    newLen = Integer.MAX_VALUE;                }                char[] buf = new char[newLen];                System.arraycopy(retCC, 0, buf, 0, retLen);                retCC = buf;            }        }        return new String(retCC, 0, retLen);    }     /**     * Appends the URL escape sequence for the specified char to the     * specified StringBuffer.     */    private static int escape(char[] cc, char c, int index) {        cc[index++] = '%';        cc[index++] = Character.forDigit((c >> 4) & 0xF, 16);        cc[index++] = Character.forDigit(c & 0xF, 16);        return index;    }     /**     * Un-escape and return the character at position i in string s.     */    private static byte unescape(String s, int i) {        return (byte) Integer.parseInt(s.substring(i+1,i+3),16);    }      /**     * Returns a new String constructed from the specified String by replacing     * the URL escape sequences and UTF8 encoding with the characters they     * represent.     */    public static String decode(String s) {        int n = s.length();        if ((n == 0) || (s.indexOf('%') < 0))            return s;         StringBuilder sb = new StringBuilder(n);        ByteBuffer bb = ByteBuffer.allocate(n);        CharBuffer cb = CharBuffer.allocate(n);        CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8")            .onMalformedInput(CodingErrorAction.REPORT)            .onUnmappableCharacter(CodingErrorAction.REPORT);         char c = s.charAt(0);        for (int i = 0; i < n;) {            assert c == s.charAt(i);            if (c != '%') {                sb.append(c);                if (++i >= n)                    break;                c = s.charAt(i);                continue;            }            bb.clear();            int ui = i;            for (;;) {                assert (n - i >= 2);                try {                    bb.put(unescape(s, i));                } catch (NumberFormatException e) {                    throw new IllegalArgumentException();                }                i += 3;                if (i >= n)                    break;                c = s.charAt(i);                if (c != '%')                    break;            }            bb.flip();            cb.clear();            dec.reset();            CoderResult cr = dec.decode(bb, cb, true);            if (cr.isError())                throw new IllegalArgumentException("Error decoding percent encoded characters");            cr = dec.flush(cb);            if (cr.isError())                throw new IllegalArgumentException("Error decoding percent encoded characters");            sb.append(cb.flip().toString());        }         return sb.toString();    }     /**     * Returns a canonical version of the specified string.     */    public String canonizeString(String file) {        int i = 0;        int lim = file.length();         // Remove embedded /../        while ((i = file.indexOf("/../")) >= 0) {            if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {                file = file.substring(0, lim) + file.substring(i + 3);            } else {                file = file.substring(i + 3);            }        }        // Remove embedded /./        while ((i = file.indexOf("/./")) >= 0) {            file = file.substring(0, i) + file.substring(i + 2);        }        // Remove trailing ..        while (file.endsWith("/..")) {            i = file.indexOf("/..");            if ((lim = file.lastIndexOf('/', i - 1)) >= 0) {                file = file.substring(0, lim+1);            } else {                file = file.substring(0, i);            }        }        // Remove trailing .        if (file.endsWith("/."))            file = file.substring(0, file.length() -1);         return file;    }     public static URL fileToEncodedURL(File file)        throws MalformedURLException    {        String path = file.getAbsolutePath();        path = ParseUtil.encodePath(path);        if (!path.startsWith("/")) {            path = "/" + path;        }        if (!path.endsWith("/") && file.isDirectory()) {            path = path + "/";        }        return new URL("file", "", path);    }     public static java.net.URI toURI(URL url) {        String protocol = url.getProtocol();        String auth = url.getAuthority();        String path = url.getPath();        String query = url.getQuery();        String ref = url.getRef();        if (path != null && !(path.startsWith("/")))            path = "/" + path;         //        // In java.net.URI class, a port number of -1 implies the default        // port number. So get it stripped off before creating URI instance.        //        if (auth != null && auth.endsWith(":-1"))            auth = auth.substring(0, auth.length() - 3);         java.net.URI uri;        try {            uri = createURI(protocol, auth, path, query, ref);        } catch (java.net.URISyntaxException e) {            uri = null;        }        return uri;    }     //    // createURI() and its auxiliary code are cloned from java.net.URI.    // Most of the code are just copy and paste, except that quote()    // has been modified to avoid double-escape.    //    // Usually it is unacceptable, but we're forced to do it because    // otherwise we need to change public API, namely java.net.URI's    // multi-argument constructors. It turns out that the changes cause    // incompatibilities so can't be done.    //    private static URI createURI(String scheme,                                 String authority,                                 String path,                                 String query,                                 String fragment) throws URISyntaxException    {        String s = toString(scheme, null,                            authority, null, null, -1,                            path, query, fragment);        checkPath(s, scheme, path);        return new URI(s);    }     private static String toString(String scheme,                            String opaquePart,                            String authority,                            String userInfo,                            String host,                            int port,                            String path,                            String query,                            String fragment)    {        StringBuffer sb = new StringBuffer();        if (scheme != null) {            sb.append(scheme);            sb.append(':');        }        appendSchemeSpecificPart(sb, opaquePart,                                 authority, userInfo, host, port,                                 path, query);        appendFragment(sb, fragment);        return sb.toString();    }     private static void appendSchemeSpecificPart(StringBuffer sb,                                          String opaquePart,                                          String authority,                                          String userInfo,                                          String host,                                          int port,                                          String path,                                          String query)    {        if (opaquePart != null) {            /* check if SSP begins with an IPv6 address             * because we must not quote a literal IPv6 address             */            if (opaquePart.startsWith("//[")) {                int end =  opaquePart.indexOf("]");                if (end != -1 && opaquePart.indexOf(":")!=-1) {                    String doquote, dontquote;                    if (end == opaquePart.length()) {                        dontquote = opaquePart;                        doquote = "";                    } else {                        dontquote = opaquePart.substring(0,end+1);                        doquote = opaquePart.substring(end+1);                    }                    sb.append (dontquote);                    sb.append(quote(doquote, L_URIC, H_URIC));                }            } else {                sb.append(quote(opaquePart, L_URIC, H_URIC));            }        } else {            appendAuthority(sb, authority, userInfo, host, port);            if (path != null)                sb.append(quote(path, L_PATH, H_PATH));            if (query != null) {                sb.append('?');                sb.append(quote(query, L_URIC, H_URIC));            }        }    }     private static void appendAuthority(StringBuffer sb,                                 String authority,                                 String userInfo,                                 String host,                                 int port)    {        if (host != null) {            sb.append("//");            if (userInfo != null) {                sb.append(quote(userInfo, L_USERINFO, H_USERINFO));                sb.append('@');            }            boolean needBrackets = ((host.indexOf(':') >= 0)                                    && !host.startsWith("[")                                    && !host.endsWith("]"));            if (needBrackets) sb.append('[');            sb.append(host);            if (needBrackets) sb.append(']');            if (port != -1) {                sb.append(':');                sb.append(port);            }        } else if (authority != null) {            sb.append("//");            if (authority.startsWith("[")) {                int end = authority.indexOf("]");                if (end != -1 && authority.indexOf(":")!=-1) {                    String doquote, dontquote;                    if (end == authority.length()) {                        dontquote = authority;                        doquote = "";                    } else {                        dontquote = authority.substring(0,end+1);                        doquote = authority.substring(end+1);                    }                    sb.append (dontquote);                    sb.append(quote(doquote,                            L_REG_NAME | L_SERVER,                            H_REG_NAME | H_SERVER));                }            } else {                sb.append(quote(authority,                            L_REG_NAME | L_SERVER,                            H_REG_NAME | H_SERVER));            }        }    }     private static void appendFragment(StringBuffer sb, String fragment) {        if (fragment != null) {            sb.append('#');            sb.append(quote(fragment, L_URIC, H_URIC));        }    }     // Quote any characters in s that are not permitted    // by the given mask pair    //    private static String quote(String s, long lowMask, long highMask) {        int n = s.length();        StringBuffer sb = null;        boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);        for (int i = 0; i < s.length(); i++) {            char c = s.charAt(i);            if (c < '\u0080') {                if (!match(c, lowMask, highMask) && !isEscaped(s, i)) {                    if (sb == null) {                        sb = new StringBuffer();                        sb.append(s.substring(0, i));                    }                    appendEscape(sb, (byte)c);                } else {                    if (sb != null)                        sb.append(c);                }            } else if (allowNonASCII                       && (Character.isSpaceChar(c)                           || Character.isISOControl(c))) {                if (sb == null) {                    sb = new StringBuffer();                    sb.append(s.substring(0, i));                }                appendEncoded(sb, c);            } else {                if (sb != null)                    sb.append(c);            }        }        return (sb == null) ? s : sb.toString();    }     //    // To check if the given string has an escaped triplet    // at the given position    //    private static boolean isEscaped(String s, int pos) {        if (s == null || (s.length() <= (pos + 2)))            return false;         return s.charAt(pos) == '%'               && match(s.charAt(pos + 1), L_HEX, H_HEX)               && match(s.charAt(pos + 2), L_HEX, H_HEX);    }     private static void appendEncoded(StringBuffer sb, char c) {        ByteBuffer bb = null;        try {            bb = ThreadLocalCoders.encoderFor("UTF-8")                .encode(CharBuffer.wrap("" + c));        } catch (CharacterCodingException x) {            assert false;        }        while (bb.hasRemaining()) {            int b = bb.get() & 0xff;            if (b >= 0x80)

Lines 1–500 of 680.