URLStreamHandler.java revision 7e00db4156e50ce5f20fefb820dca339299134d3
1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.net;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
21d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilsonimport java.nio.charset.Charsets;
22e32b21f14d52bac429a9c54fe031f9e92c911d64Jesse Wilsonimport libcore.base.Objects;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.apache.harmony.luni.util.URLUtil;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The abstract class {@code URLStreamHandler} is the base for all classes which
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * can handle the communication with a URL object over a particular protocol
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * type.
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic abstract class URLStreamHandler {
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Establishes a new connection to the resource specified by the URL {@code
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * u}. Since different protocols also have unique ways of connecting, it
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * must be overwritten by the subclass.
35f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param u
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL to the resource where a connection has to be opened.
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the opened URLConnection to the specified resource.
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an I/O error occurs during opening the connection.
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected abstract URLConnection openConnection(URL u) throws IOException;
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Establishes a new connection to the resource specified by the URL {@code
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * u} using the given {@code proxy}. Since different protocols also have
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * unique ways of connecting, it must be overwritten by the subclass.
48f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param u
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL to the resource where a connection has to be opened.
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param proxy
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the proxy that is used to make the connection.
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the opened URLConnection to the specified resource.
54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if an I/O error occurs during opening the connection.
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalArgumentException
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if any argument is {@code null} or the type of proxy is
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             wrong.
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws UnsupportedOperationException
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             if the protocol handler doesn't support this method.
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
62b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes    protected URLConnection openConnection(URL u, Proxy proxy) throws IOException {
63b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes        throw new UnsupportedOperationException();
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Parses the clear text URL in {@code str} into a URL object. URL strings
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * generally have the following format:
69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <p>
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * http://www.company.com/java/file1.java#reference
71f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * <p>
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * The string is parsed in HTTP format. If the protocol has a different URL
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * format this method must be overridden.
74f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param u
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL to fill in the parsed clear text URL parts.
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param str
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL string that is to be parsed.
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param start
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the string position from where to begin parsing.
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param end
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the string position to stop parsing.
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #toExternalForm
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see URL
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected void parseURL(URL u, String str, int start, int end) {
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // For compatibility, refer to Harmony-2941
88f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (str.startsWith("//", start)
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && str.indexOf('/', start + 2) == -1
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && end <= Integer.MIN_VALUE + 1) {
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new StringIndexOutOfBoundsException(end - 2 - start);
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (end < start) {
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (this != u.strmHandler) {
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                throw new SecurityException();
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return;
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
99f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        String parseString = "";
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (start < end) {
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            parseString = str.substring(start, end);
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        end -= start;
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int fileIdx = 0;
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Default is to use info from context
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String host = u.getHost();
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int port = u.getPort();
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String ref = u.getRef();
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String file = u.getPath();
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String query = u.getQuery();
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String authority = u.getAuthority();
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String userInfo = u.getUserInfo();
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int refIdx = parseString.indexOf('#', 0);
116f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        if (parseString.startsWith("//")) {
1178fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            int hostIdx = 2;
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            port = -1;
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            fileIdx = parseString.indexOf('/', hostIdx);
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int questionMarkIndex = parseString.indexOf('?', hostIdx);
1218fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            if (questionMarkIndex != -1 && (fileIdx == -1 || fileIdx > questionMarkIndex)) {
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                fileIdx = questionMarkIndex;
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (fileIdx == -1) {
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                fileIdx = end;
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // Use default
127f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                file = "";
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int hostEnd = fileIdx;
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (refIdx != -1 && refIdx < fileIdx) {
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                hostEnd = refIdx;
1328fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                fileIdx = refIdx;
1338fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                file = "";
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int userIdx = parseString.lastIndexOf('@', hostEnd);
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            authority = parseString.substring(hostIdx, hostEnd);
1378fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            if (userIdx != -1) {
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                userInfo = parseString.substring(hostIdx, userIdx);
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                hostIdx = userIdx + 1;
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1428fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            int endOfIPv6Addr = parseString.indexOf(']', hostIdx);
1438fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            if (endOfIPv6Addr >= hostEnd) {
1448fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                endOfIPv6Addr = -1;
1458fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            }
1468fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson
1478fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            // the port separator must be immediately after an IPv6 address "http://[::1]:80/"
1488fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            int portIdx = -1;
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (endOfIPv6Addr != -1) {
1508fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                int maybeColon = endOfIPv6Addr + 1;
1518fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                if (maybeColon < hostEnd && parseString.charAt(maybeColon) == ':') {
1528fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                    portIdx = maybeColon;
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1548fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            } else {
1558fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson                portIdx = parseString.indexOf(':', hostIdx);
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1588fd0225a5c3918fe0cd4680258388985c25533e5Jesse Wilson            if (portIdx == -1 || portIdx > hostEnd) {
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                host = parseString.substring(hostIdx, hostEnd);
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                host = parseString.substring(hostIdx, portIdx);
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                String portString = parseString.substring(portIdx + 1, hostEnd);
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (portString.length() == 0) {
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    port = -1;
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } else {
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    port = Integer.parseInt(portString);
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (refIdx > -1) {
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            ref = parseString.substring(refIdx + 1, end);
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int fileEnd = (refIdx == -1 ? end : refIdx);
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int queryIdx = parseString.lastIndexOf('?', fileEnd);
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean canonicalize = false;
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (queryIdx > -1) {
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            query = parseString.substring(queryIdx + 1, fileEnd);
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (queryIdx == 0 && file != null) {
18180a7fbab52b96c9fd47c72f8987d1babe2cd001dElliott Hughes                if (file.isEmpty()) {
182f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    file = "/";
183f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                } else if (file.startsWith("/")) {
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    canonicalize = true;
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int last = file.lastIndexOf('/') + 1;
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                file = file.substring(0, last);
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            fileEnd = queryIdx;
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Don't inherit query unless only the ref is changed
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (refIdx != 0) {
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            query = null;
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (fileIdx > -1) {
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (fileIdx < end && parseString.charAt(fileIdx) == '/') {
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                file = parseString.substring(fileIdx, fileEnd);
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else if (fileEnd > fileIdx) {
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (file == null) {
201f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    file = "";
20280a7fbab52b96c9fd47c72f8987d1babe2cd001dElliott Hughes                } else if (file.isEmpty()) {
203f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    file = "/";
204f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                } else if (file.startsWith("/")) {
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    canonicalize = true;
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int last = file.lastIndexOf('/') + 1;
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (last == 0) {
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    file = parseString.substring(fileIdx, fileEnd);
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } else {
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    file = file.substring(0, last)
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            + parseString.substring(fileIdx, fileEnd);
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (file == null) {
217f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            file = "";
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (host == null) {
221f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            host = "";
222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (canonicalize) {
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // modify file if there's any relative referencing
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            file = URLUtil.canonicalizePath(file);
227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        setURL(u, u.getProtocol(), host, port, authority, userInfo, file,
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                query, ref);
231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Sets the fields of the URL {@code u} to the values of the supplied
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * arguments.
236f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param u
238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the non-null URL object to be set.
239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param protocol
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the protocol.
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param host
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the host name.
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param port
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the port number.
245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param file
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the file component.
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param ref
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the reference.
249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @deprecated use setURL(URL, String String, int, String, String, String,
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             String, String) instead.
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Deprecated
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected void setURL(URL u, String protocol, String host, int port,
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String file, String ref) {
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (this != u.strmHandler) {
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new SecurityException();
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        u.set(protocol, host, port, file, ref);
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Sets the fields of the URL {@code u} to the values of the supplied
263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * arguments.
264f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param u
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the non-null URL object to be set.
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param protocol
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the protocol.
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param host
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the host name.
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param port
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the port number.
273adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param authority
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the authority.
275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param userInfo
276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the user info.
277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param file
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the file component.
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param query
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the query.
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param ref
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the reference.
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected void setURL(URL u, String protocol, String host, int port,
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String authority, String userInfo, String file, String query,
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String ref) {
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (this != u.strmHandler) {
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new SecurityException();
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        u.set(protocol, host, port, authority, userInfo, file, query, ref);
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the clear text representation of a given URL using HTTP format.
295f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the URL object to be converted.
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the clear text representation of the specified URL.
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #parseURL
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see URL#toExternalForm()
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected String toExternalForm(URL url) {
303d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        return toExternalForm(url, false);
304d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    }
305d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
306d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    String toExternalForm(URL url, boolean escapeIllegalCharacters) {
307d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        StringBuilder result = new StringBuilder();
308d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        result.append(url.getProtocol());
309d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        result.append(':');
310d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String authority = url.getAuthority();
312d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        if (authority != null && !authority.isEmpty()) {
313d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append("//");
314d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (escapeIllegalCharacters) {
315d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                authority = fixEncoding(authority, "$,;@&=+:[]");
316d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
317d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append(authority);
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
320d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        String fileAndQuery = url.getFile();
321d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        if (fileAndQuery != null) {
322d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (escapeIllegalCharacters) {
323d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                fileAndQuery = fixEncoding(fileAndQuery, "$,;@&=+:/?");
324d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
325d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append(fileAndQuery);
326a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson        }
327d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
328d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        String ref = url.getRef();
329adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (ref != null) {
330d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append('#');
331d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (escapeIllegalCharacters) {
332d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                ref = fixEncoding(ref, "$,;@&=+:/?[]");
333d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
334d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append(ref);
335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
336d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
337d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        return result.toString();
338d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    }
339d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
340d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    /**
341d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * Escapes the unescaped characters of {@code s} that are not permitted.
342d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * Permitted characters are:
343d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * <ul>
344d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     *   <li>Unreserved characters in RFC 2396.
345d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     *   <li>{@code extraOkayChars},
346d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     *   <li>non-ASCII, non-control, non-whitespace characters
347d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * </ul>
348d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     *
349d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * <p>Unlike the methods in {@code URI}, this method ignores input that has
350d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * already been escaped. For example, input of "hello%20world" is unchanged
351d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * by this method but would be double-escaped to "hello%2520world" by URI.
352d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     *
353d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * <p>UTF-8 is used to encode escaped characters. A single input character
354d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     * like "\u0080" may be encoded to multiple octets like %C2%80.
355d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson     */
356d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    private String fixEncoding(String s, String extraPermittedChars) {
357d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        StringBuilder result = null;
358d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        int copiedCount = 0;
359d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
360d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        for (int i = 0; i < s.length(); i++) {
361d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            char c = s.charAt(i);
362d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
363d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (c == '%') {
364d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                i += 2; // this is a 3-character sequence like "%20"
365d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                continue;
366d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
367d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
368d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            // unreserved characters: alphanum | mark
369d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'
370d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                    || c == '-' || c == '_' || c == '.' || c == '!' || c == '~'
371d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                    || c == '*' || c == '\'' || c == '(' || c == ')') {
372d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                continue;
373d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
374d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
375d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            // characters permitted in this context
376d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (extraPermittedChars.indexOf(c) != -1) {
377d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                continue;
378d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
379d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
380d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            // other characters
381d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (c > 0x7f && !Character.isISOControl(c) && !Character.isSpaceChar(c)) {
382d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                continue;
383d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
384d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
385d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            /*
386d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson             * We've encountered a character that must be escaped.
387d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson             */
388d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (result == null) {
389d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                result = new StringBuilder();
390d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
391d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append(s, copiedCount, i);
392d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
393d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            if (c < 0x7f) {
3944b25199bc0b7a64a6feaa60e7d5d6b0474341234Elliott Hughes                URIEncoderDecoder.appendHex(result, (byte) c);
395d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            } else {
396d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                for (byte b : s.substring(i, i + 1).getBytes(Charsets.UTF_8)) {
3974b25199bc0b7a64a6feaa60e7d5d6b0474341234Elliott Hughes                    URIEncoderDecoder.appendHex(result, b);
398d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson                }
399d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            }
400d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
401d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            copiedCount = i + 1;
402d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        }
403d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
404d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        if (result == null) {
405d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            return s;
406d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        } else {
407d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            result.append(s, copiedCount, s.length());
408d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson            return result.toString();
409d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson        }
410d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson    }
411d1b5e5da828434388e486a388710d21e4306dae0Jesse Wilson
412adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
4137e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * Returns true if {@code a} and {@code b} have the same protocol, host,
4147e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * port, file, and reference.
415adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
4167e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson    protected boolean equals(URL a, URL b) {
4177e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson        return sameFile(a, b)
4187e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson                && Objects.equal(a.getRef(), b.getRef())
4197e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson                && Objects.equal(a.getQuery(), b.getQuery());
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
422adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the default port of the protocol used by the handled URL. The
4247e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * default implementation always returns {@code -1}.
425adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected int getDefaultPort() {
427adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return -1;
428adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
429adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
4317e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * Returns the host address of {@code url}.
432adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
433adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected InetAddress getHostAddress(URL url) {
434adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
435adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String host = url.getHost();
436adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (host == null || host.length() == 0) {
437adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return null;
438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
439adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return InetAddress.getByName(host);
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (UnknownHostException e) {
441adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return null;
442adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
443adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
444adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
445adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
4467e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * Returns the hash code of {@code url}.
447adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
448adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected int hashCode(URL url) {
449adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return toExternalForm(url).hashCode();
450adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
451adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
452adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
4537e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * Returns true if the hosts of {@code a} and {@code b} are equal.
454adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
45567db542e53c47a28e3f96beb7c9e46330483dc40Jesse Wilson    protected boolean hostsEqual(URL a, URL b) {
4567e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson        // URLs with the same case-insensitive host name have equal hosts
45767db542e53c47a28e3f96beb7c9e46330483dc40Jesse Wilson        String aHost = getHost(a);
45867db542e53c47a28e3f96beb7c9e46330483dc40Jesse Wilson        String bHost = getHost(b);
4597e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson        return aHost != null && aHost.equalsIgnoreCase(bHost);
460adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
461adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
462adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
4637e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * Returns true if {@code a} and {@code b} have the same protocol, host,
4647e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson     * port and file.
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
4667e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson    protected boolean sameFile(URL a, URL b) {
4677e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson        return Objects.equal(a.getProtocol(), b.getProtocol())
4687e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson                && hostsEqual(a, b)
4697e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson                && a.getEffectivePort() == b.getEffectivePort()
4707e00db4156e50ce5f20fefb820dca339299134d3Jesse Wilson                && Objects.equal(a.getFile(), b.getFile());
471adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
472adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
473adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static String getHost(URL url) {
474adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String host = url.getHost();
47580a7fbab52b96c9fd47c72f8987d1babe2cd001dElliott Hughes        if ("file".equals(url.getProtocol()) && host.isEmpty()) {
476f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            host = "localhost";
477adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return host;
479adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
480adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
481