19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.net;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedyimport android.annotation.Nullable;
20fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkeyimport android.content.Intent;
2165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkeyimport android.os.Environment;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
24a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkeyimport android.os.StrictMode;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
26846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
27e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedyimport libcore.net.UriCodec;
28e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
3065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkeyimport java.io.IOException;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.UnsupportedEncodingException;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.net.URLEncoder;
33d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.AbstractList;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Collections;
3758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodsonimport java.util.LinkedHashSet;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
39ccae412deda8b0c165c86f395752c0667a3411a6Nick Pellyimport java.util.Locale;
40846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkeyimport java.util.Objects;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.RandomAccess;
4258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodsonimport java.util.Set;
43846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Immutable URI reference. A URI reference includes a URI and a fragment, the
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * component of the URI following a '#'. Builds and parses URI references
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * which conform to
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <a href="http://www.faqs.org/rfcs/rfc2396.html">RFC 2396</a>.
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>In the interest of performance, this class performs little to no
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * validation. Behavior is undefined for invalid input. This class is very
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * forgiving--in the face of invalid input, it will return garbage
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * rather than throw an exception unless otherwise specified.
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class Uri implements Parcelable, Comparable<Uri> {
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /*
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    This class aims to do as little up front work as possible. To accomplish
6058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson    that, we vary the implementation depending on what the user passes in.
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    For example, we have one implementation if the user passes in a
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    URI string (StringUri) and another if the user passes in the
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    individual components (OpaqueUri).
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    *Concurrency notes*: Like any truly immutable object, this class is safe
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for concurrent use. This class uses a caching pattern in some places where
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    it doesn't use volatile or synchronized. This is safe to do with ints
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    because getting or setting an int is atomic. It's safe to do with a String
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    because the internal fields are final and the memory model guarantees other
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    threads won't see a partially initialized instance. We are not guaranteed
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    that some threads will immediately see changes from other threads on
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    certain platforms, but we don't mind if those threads reconstruct the
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    cached result. As a result, we get thread safe caching with no concurrency
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    overhead, which means the most common case, access from a single thread,
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    is as fast as possible.
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    From the Java Language spec.:
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    "17.5 Final Field Semantics
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    ... when the object is seen by another thread, that thread will always
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    see the correctly constructed version of that object's final fields.
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    It will also see versions of any object or array referenced by
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    those final fields that are at least as up-to-date as the final fields
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    are."
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    In that same vein, all non-transient fields within Uri
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    implementations should be final and immutable so as to ensure true
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    immutability for clients even when they don't use proper concurrency
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    control.
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    For reference, from RFC 2396:
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    "4.3. Parsing a URI Reference
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       A URI reference is typically parsed according to the four main
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       components and fragment identifier in order to determine what
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       components are present and whether the reference is relative or
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       absolute.  The individual components are then parsed for their
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       subparts and, if not opaque, to verify their validity.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       Although the BNF defines what is allowed in each component, it is
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       ambiguous in terms of differentiating between an authority component
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       and a path component that begins with two slash characters.  The
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       greedy algorithm is used for disambiguation: the left-most matching
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       rule soaks up as much of the URI reference string as it is capable of
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       matching.  In other words, the authority component wins."
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    The "four main components" of a hierarchical URI consist of
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    <scheme>://<authority><path>?<query>
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Log tag. */
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String LOG = Uri.class.getSimpleName();
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
118a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * NOTE: EMPTY accesses this field during its own initialization, so this
119a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * field *must* be initialized first, or else EMPTY will see a null value!
120a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     *
121a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * Placeholder for strings which haven't been cached. This enables us
122a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * to cache null. We intentionally create a new String instance so we can
123a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * compare its identity and there is no chance we will confuse it with
124a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     * user data.
125a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru     */
126a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru    @SuppressWarnings("RedundantStringConstructorCall")
127a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru    private static final String NOT_CACHED = new String("NOT CACHED");
128a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru
129a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru    /**
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The empty URI, equivalent to "".
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final Uri EMPTY = new HierarchicalUri(null, Part.NULL,
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            PathPart.EMPTY, Part.NULL, Part.NULL);
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Prevents external subclassing.
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri() {}
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if this URI is hierarchical like "http://google.com".
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Absolute URIs are hierarchical if the scheme-specific part starts with
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a '/'. Relative URIs are always hierarchical.
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract boolean isHierarchical();
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if this URI is opaque like "mailto:nobody@google.com". The
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * scheme-specific part of an opaque URI cannot start with a '/'.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isOpaque() {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return !isHierarchical();
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1564465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * Returns true if this URI is relative, i.e.&nbsp;if it doesn't contain an
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * explicit scheme.
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if this URI is relative, false if it's absolute
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract boolean isRelative();
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1644465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * Returns true if this URI is absolute, i.e.&nbsp;if it contains an
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * explicit scheme.
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if this URI is absolute, false if it's relative
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isAbsolute() {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return !isRelative();
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the scheme of this URI. Example: "http"
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the scheme or null if this is a relative URI
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
178e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getScheme();
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1824465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * Gets the scheme-specific part of this URI, i.e.&nbsp;everything between
1834465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * the scheme separator ':' and the fragment separator '#'. If this is a
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * relative URI, this method returns the entire URI. Decodes escaped octets.
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Example: "//www.google.com/search?q=android"
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded scheme-specific-part
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getSchemeSpecificPart();
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1934465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * Gets the scheme-specific part of this URI, i.e.&nbsp;everything between
1944465d1a03ee5fddc5987c19fc36b0bb79e19572fNewton Allen     * the scheme separator ':' and the fragment separator '#'. If this is a
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * relative URI, this method returns the entire URI. Leaves escaped octets
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * intact.
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Example: "//www.google.com/search?q=android"
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded scheme-specific-part
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedSchemeSpecificPart();
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded authority part of this URI. For
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * server addresses, the authority is structured as follows:
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@code [ userinfo '@' ] host [ ':' port ]}
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Examples: "google.com", "bob@google.com:80"
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the authority for this URI or null if not present
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
213e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getAuthority();
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded authority part of this URI. For
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * server addresses, the authority is structured as follows:
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@code [ userinfo '@' ] host [ ':' port ]}
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Examples: "google.com", "bob@google.com:80"
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the authority for this URI or null if not present
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
225e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedAuthority();
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded user information from the authority.
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * For example, if the authority is "nobody@google.com", this method will
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * return "nobody".
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the user info for this URI or null if not present
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
235e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getUserInfo();
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded user information from the authority.
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * For example, if the authority is "nobody@google.com", this method will
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * return "nobody".
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the user info for this URI or null if not present
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
245e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedUserInfo();
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded host from the authority for this URI. For example,
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * if the authority is "bob@google.com", this method will return
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "google.com".
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the host for this URI or null if not present
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
255e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getHost();
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the port from the authority for this URI. For example,
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * if the authority is "google.com:80", this method will return 80.
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the port for this URI or -1 if invalid or not present
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract int getPort();
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded path.
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded path, or null if this is not a hierarchical URI
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (like "mailto:nobody@google.com") or the URI is invalid
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
272e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getPath();
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded path.
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the encoded path, or null if this is not a hierarchical URI
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (like "mailto:nobody@google.com") or the URI is invalid
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
281e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedPath();
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded query component from this URI. The query comes after
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the query separator ('?') and before the fragment separator ('#'). This
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * method would return "q=android" for
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "http://www.google.com/search?q=android".
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded query or null if there isn't one
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
292e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getQuery();
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded query component from this URI. The query comes after
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the query separator ('?') and before the fragment separator ('#'). This
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * method would return "q=android" for
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "http://www.google.com/search?q=android".
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the encoded query or null if there isn't one
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
303e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedQuery();
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded fragment part of this URI, everything after the '#'.
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded fragment or null if there isn't one
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
311e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getFragment();
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the encoded fragment part of this URI, everything after the '#'.
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the encoded fragment or null if there isn't one
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
319e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getEncodedFragment();
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded path segments.
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return decoded path segments, each without a leading or trailing '/'
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract List<String> getPathSegments();
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the decoded last segment in the path.
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded last segment or null if the path is empty
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
334e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String getLastPathSegment();
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Compares this Uri to another object for equality. Returns true if the
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * encoded string representations of this Uri and the given Uri are
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * equal. Case counts. Paths are not normalized. If one Uri specifies a
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * default port explicitly and the other leaves it implicit, they will not
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be considered equal.
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean equals(Object o) {
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!(o instanceof Uri)) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Uri other = (Uri) o;
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return toString().equals(other.toString());
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Hashes the encoded string represention of this Uri consistently with
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #equals(Object)}.
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int hashCode() {
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return toString().hashCode();
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Compares the string representation of this Uri with that of
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * another.
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int compareTo(Uri other) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return toString().compareTo(other.toString());
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the encoded string representation of this URI.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Example: "http://google.com/"
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract String toString();
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
37790c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn     * Return a string representation of the URI that is safe to print
37890c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn     * to logs and other places where PII should be avoided.
37990c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn     * @hide
38090c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn     */
38190c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn    public String toSafeString() {
38290c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        String scheme = getScheme();
38390c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        String ssp = getSchemeSpecificPart();
38490c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        if (scheme != null) {
38590c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn            if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
38690c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                    || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
387ba4e877905cca9656d9f13030625717619bc163cAkinobu Nakashima                    || scheme.equalsIgnoreCase("mailto") || scheme.equalsIgnoreCase("nfc")) {
38890c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                StringBuilder builder = new StringBuilder(64);
38990c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                builder.append(scheme);
39090c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                builder.append(':');
39190c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                if (ssp != null) {
39290c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                    for (int i=0; i<ssp.length(); i++) {
39390c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                        char c = ssp.charAt(i);
39490c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                        if (c == '-' || c == '@' || c == '.') {
39590c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                            builder.append(c);
39690c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                        } else {
39790c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                            builder.append('x');
39890c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                        }
39990c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                    }
40090c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                }
40190c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn                return builder.toString();
4023f24a1d94a42762c245a32272c797250a804cfc3Alex Klyubin            } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")
4033f24a1d94a42762c245a32272c797250a804cfc3Alex Klyubin                    || scheme.equalsIgnoreCase("ftp")) {
4043f24a1d94a42762c245a32272c797250a804cfc3Alex Klyubin                ssp = "//" + ((getHost() != null) ? getHost() : "")
4053f24a1d94a42762c245a32272c797250a804cfc3Alex Klyubin                        + ((getPort() != -1) ? (":" + getPort()) : "")
4063f24a1d94a42762c245a32272c797250a804cfc3Alex Klyubin                        + "/...";
40790c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn            }
40890c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        }
40990c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        // Not a sensitive scheme, but let's still be conservative about
41090c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        // the data we include -- only the ssp, not the query params or
41190c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        // fragment, because those can often have sensitive info.
41290c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        StringBuilder builder = new StringBuilder(64);
41390c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        if (scheme != null) {
41490c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn            builder.append(scheme);
41590c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn            builder.append(':');
41690c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        }
41790c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        if (ssp != null) {
41890c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn            builder.append(ssp);
41990c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        }
42090c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn        return builder.toString();
42190c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn    }
42290c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn
42390c52de28691ca0bbbf7c039ef20f85ce46882ccDianne Hackborn    /**
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Constructs a new builder, copying the attributes from this Uri.
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public abstract Builder buildUpon();
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Index of a component which was not found. */
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static int NOT_FOUND = -1;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Placeholder value for an index which hasn't been calculated yet. */
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static int NOT_CALCULATED = -2;
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Error message presented when a user tries to treat an opaque URI as
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * hierarchical.
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NOT_HIERARCHICAL
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = "This isn't a hierarchical URI.";
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Default encoding. */
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String DEFAULT_ENCODING = "UTF-8";
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a Uri which parses the given encoded URI string.
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4478aa393b03f6d1f25313c658334e9b86a86a6f61fSimon Schoar     * @param uriString an RFC 2396-compliant, encoded URI
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if uriString is null
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Uri for this given uri string
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Uri parse(String uriString) {
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new StringUri(uriString);
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a Uri from a file. The URI has the form
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "file://<absolute path>". Encodes path characters with the exception of
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * '/'.
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Example: "file:///tmp/android.txt"
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if file is null
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return a Uri for the given file
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Uri fromFile(File file) {
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (file == null) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new NullPointerException("file");
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new HierarchicalUri(
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                "file", Part.EMPTY, path, Part.NULL, Part.NULL);
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * An implementation which wraps a String URI. This URI can be opaque or
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * hierarchical, but we extend AbstractHierarchicalUri in case we need
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the hierarchical functionality.
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class StringUri extends AbstractHierarchicalUri {
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Used in parcelling. */
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final int TYPE_ID = 1;
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** URI string representation. */
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String uriString;
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private StringUri(String uriString) {
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (uriString == null) {
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new NullPointerException("uriString");
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.uriString = uriString;
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Uri readFrom(Parcel parcel) {
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new StringUri(parcel.readString());
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int describeContents() {
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel parcel, int flags) {
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeInt(TYPE_ID);
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeString(uriString);
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Cached scheme separator index. */
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile int cachedSsi = NOT_CALCULATED;
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Finds the first ':'. Returns -1 if none found. */
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int findSchemeSeparator() {
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cachedSsi == NOT_CALCULATED
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? cachedSsi = uriString.indexOf(':')
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : cachedSsi;
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Cached fragment separator index. */
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile int cachedFsi = NOT_CALCULATED;
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Finds the first '#'. Returns -1 if none found. */
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int findFragmentSeparator() {
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cachedFsi == NOT_CALCULATED
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? cachedFsi = uriString.indexOf('#', findSchemeSeparator())
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : cachedFsi;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isHierarchical() {
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ssi = findSchemeSeparator();
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ssi == NOT_FOUND) {
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // All relative URIs are hierarchical.
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (uriString.length() == ssi + 1) {
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // No ssp.
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If the ssp starts with a '/', this is hierarchical.
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return uriString.charAt(ssi + 1) == '/';
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isRelative() {
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Note: We return true if the index is 0
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return findSchemeSeparator() == NOT_FOUND;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile String scheme = NOT_CACHED;
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getScheme() {
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean cached = (scheme != NOT_CACHED);
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cached ? scheme : (scheme = parseScheme());
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseScheme() {
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ssi = findSchemeSeparator();
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ssi == NOT_FOUND ? null : uriString.substring(0, ssi);
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part ssp;
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getSsp() {
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ssp == null ? ssp = Part.fromEncoded(parseSsp()) : ssp;
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedSchemeSpecificPart() {
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getSsp().getEncoded();
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getSchemeSpecificPart() {
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getSsp().getDecoded();
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseSsp() {
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ssi = findSchemeSeparator();
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int fsi = findFragmentSeparator();
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Return everything between ssi and fsi.
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fsi == NOT_FOUND
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? uriString.substring(ssi + 1)
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : uriString.substring(ssi + 1, fsi);
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part authority;
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getAuthorityPart() {
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (authority == null) {
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String encodedAuthority
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        = parseAuthority(this.uriString, findSchemeSeparator());
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return authority = Part.fromEncoded(encodedAuthority);
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return authority;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedAuthority() {
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getAuthorityPart().getEncoded();
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getAuthority() {
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getAuthorityPart().getDecoded();
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private PathPart path;
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private PathPart getPathPart() {
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path == null
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? path = PathPart.fromEncoded(parsePath())
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : path;
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getPath() {
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getPathPart().getDecoded();
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedPath() {
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getPathPart().getEncoded();
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<String> getPathSegments() {
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getPathPart().getPathSegments();
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parsePath() {
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String uriString = this.uriString;
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int ssi = findSchemeSeparator();
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If the URI is absolute.
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ssi > -1) {
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Is there anything after the ':'?
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean schemeOnly = ssi + 1 == uriString.length();
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (schemeOnly) {
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Opaque URI.
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // A '/' after the ':' means this is hierarchical.
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (uriString.charAt(ssi + 1) != '/') {
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Opaque URI.
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // All relative URIs are hierarchical.
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return parsePath(uriString, ssi);
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part query;
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getQueryPart() {
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return query == null
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? query = Part.fromEncoded(parseQuery()) : query;
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedQuery() {
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getQueryPart().getEncoded();
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseQuery() {
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // It doesn't make sense to cache this index. We only ever
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // calculate it once.
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int qsi = uriString.indexOf('?', findSchemeSeparator());
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (qsi == NOT_FOUND) {
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int fsi = findFragmentSeparator();
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fsi == NOT_FOUND) {
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return uriString.substring(qsi + 1);
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fsi < qsi) {
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Invalid.
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return uriString.substring(qsi + 1, fsi);
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getQuery() {
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getQueryPart().getDecoded();
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part fragment;
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getFragmentPart() {
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fragment == null
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? fragment = Part.fromEncoded(parseFragment()) : fragment;
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedFragment() {
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getFragmentPart().getEncoded();
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseFragment() {
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int fsi = findFragmentSeparator();
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fsi == NOT_FOUND ? null : uriString.substring(fsi + 1);
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getFragment() {
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getFragmentPart().getDecoded();
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return uriString;
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Parses an authority out of the given URI string.
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param uriString URI string
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param ssi scheme separator index, -1 for a relative URI
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return the authority or null if none is found
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static String parseAuthority(String uriString, int ssi) {
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = uriString.length();
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If "//" follows the scheme separator, we have an authority.
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (length > ssi + 2
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && uriString.charAt(ssi + 1) == '/'
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && uriString.charAt(ssi + 2) == '/') {
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // We have an authority.
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Look for the start of the path, query, or fragment, or the
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // end of the string.
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ssi + 3;
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LOOP: while (end < length) {
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    switch (uriString.charAt(end)) {
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '/': // Start of path
737fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                        case '\\':// Start of path
738fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // Per http://url.spec.whatwg.org/#host-state, the \ character
739fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // is treated as if it were a / character when encountered in a
740fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // host
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '?': // Start of query
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '#': // Start of fragment
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break LOOP;
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    end++;
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return uriString.substring(ssi + 3, end);
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Parses a path out of this given URI string.
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param uriString URI string
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param ssi scheme separator index, -1 for a relative URI
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return the path
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static String parsePath(String uriString, int ssi) {
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = uriString.length();
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find start of path.
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int pathStart;
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (length > ssi + 2
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && uriString.charAt(ssi + 1) == '/'
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && uriString.charAt(ssi + 2) == '/') {
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Skip over authority to path.
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                pathStart = ssi + 3;
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LOOP: while (pathStart < length) {
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    switch (uriString.charAt(pathStart)) {
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '?': // Start of query
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '#': // Start of fragment
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            return ""; // Empty path.
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        case '/': // Start of path!
779fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                        case '\\':// Start of path!
780fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // Per http://url.spec.whatwg.org/#host-state, the \ character
781fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // is treated as if it were a / character when encountered in a
782fa3afbd0e7a9a0d8fc8c55ceefdb4ddf9d0115afAdam Vartanian                          // host
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break LOOP;
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    pathStart++;
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Path starts immediately after scheme separator.
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                pathStart = ssi + 1;
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find end of path.
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int pathEnd = pathStart;
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            LOOP: while (pathEnd < length) {
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (uriString.charAt(pathEnd)) {
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case '?': // Start of query
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case '#': // Start of fragment
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break LOOP;
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                pathEnd++;
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return uriString.substring(pathStart, pathEnd);
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder buildUpon() {
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (isHierarchical()) {
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new Builder()
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .scheme(getScheme())
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .authority(getAuthorityPart())
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .path(getPathPart())
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .query(getQueryPart())
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .fragment(getFragmentPart());
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new Builder()
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .scheme(getScheme())
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .opaquePart(getSsp())
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        .fragment(getFragmentPart());
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates an opaque Uri from the given components. Encodes the ssp
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * which means this method cannot be used to create hierarchical URIs.
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param scheme of the URI
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param ssp scheme-specific-part, everything between the
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  scheme separator (':') and the fragment separator ('#'), which will
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  get encoded
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param fragment fragment, everything after the '#', null if undefined,
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  will get encoded
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if scheme or ssp is null
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Uri composed of the given scheme, ssp, and fragment
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see Builder if you don't want the ssp and fragment to be encoded
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Uri fromParts(String scheme, String ssp,
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String fragment) {
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (scheme == null) {
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new NullPointerException("scheme");
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ssp == null) {
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new NullPointerException("ssp");
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new OpaqueUri(scheme, Part.fromDecoded(ssp),
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.fromDecoded(fragment));
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Opaque URI.
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class OpaqueUri extends Uri {
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Used in parcelling. */
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final int TYPE_ID = 2;
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String scheme;
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Part ssp;
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Part fragment;
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private OpaqueUri(String scheme, Part ssp, Part fragment) {
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.scheme = scheme;
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.ssp = ssp;
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.fragment = fragment == null ? Part.NULL : fragment;
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Uri readFrom(Parcel parcel) {
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new OpaqueUri(
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.readString(),
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.readFrom(parcel),
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.readFrom(parcel)
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            );
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int describeContents() {
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel parcel, int flags) {
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeInt(TYPE_ID);
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeString(scheme);
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ssp.writeTo(parcel);
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fragment.writeTo(parcel);
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isHierarchical() {
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isRelative() {
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return scheme == null;
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getScheme() {
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.scheme;
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedSchemeSpecificPart() {
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ssp.getEncoded();
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getSchemeSpecificPart() {
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ssp.getDecoded();
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getAuthority() {
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedAuthority() {
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getPath() {
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedPath() {
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getQuery() {
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedQuery() {
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getFragment() {
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fragment.getDecoded();
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedFragment() {
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fragment.getEncoded();
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<String> getPathSegments() {
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return Collections.emptyList();
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLastPathSegment() {
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getUserInfo() {
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedUserInfo() {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getHost() {
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getPort() {
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile String cachedString = NOT_CACHED;
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean cached = cachedString != NOT_CACHED;
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cached) {
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return cachedString;
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder sb = new StringBuilder();
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(scheme).append(':');
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(getEncodedSchemeSpecificPart());
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!fragment.isEmpty()) {
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append('#').append(fragment.getEncoded());
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cachedString = sb.toString();
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder buildUpon() {
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new Builder()
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .scheme(this.scheme)
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .opaquePart(this.ssp)
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .fragment(this.fragment);
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Wrapper for path segment array.
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static class PathSegments extends AbstractList<String>
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            implements RandomAccess {
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final PathSegments EMPTY = new PathSegments(null, 0);
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final String[] segments;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int size;
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PathSegments(String[] segments, int size) {
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.segments = segments;
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.size = size;
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String get(int index) {
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (index >= size) {
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IndexOutOfBoundsException();
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return segments[index];
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int size() {
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.size;
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Builds PathSegments.
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static class PathSegmentsBuilder {
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String[] segments;
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int size = 0;
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void add(String segment) {
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (segments == null) {
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                segments = new String[4];
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (size + 1 == segments.length) {
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String[] expanded = new String[segments.length * 2];
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(segments, 0, expanded, 0, segments.length);
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                segments = expanded;
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            segments[size++] = segment;
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PathSegments build() {
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (segments == null) {
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return PathSegments.EMPTY;
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new PathSegments(segments, size);
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } finally {
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Makes sure this doesn't get reused.
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                segments = null;
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Support for hierarchical URIs.
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private abstract static class AbstractHierarchicalUri extends Uri {
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLastPathSegment() {
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // TODO: If we haven't parsed all of the segments already, just
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // grab the last one directly so we only allocate one string.
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            List<String> segments = getPathSegments();
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int size = segments.size();
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (size == 0) {
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return segments.get(size - 1);
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part userInfo;
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getUserInfoPart() {
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return userInfo == null
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? userInfo = Part.fromEncoded(parseUserInfo()) : userInfo;
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public final String getEncodedUserInfo() {
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getUserInfoPart().getEncoded();
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseUserInfo() {
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String authority = getEncodedAuthority();
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (authority == null) {
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1091cd6228dd377b2a0caa02a1e6df92f3d9ae702a95Adam Vartanian            int end = authority.lastIndexOf('@');
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return end == NOT_FOUND ? null : authority.substring(0, end);
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getUserInfo() {
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getUserInfoPart().getDecoded();
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile String host = NOT_CACHED;
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getHost() {
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean cached = (host != NOT_CACHED);
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cached ? host
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : (host = parseHost());
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String parseHost() {
1109fc01794f33057862a361a0d0113630c58befc21bKenny Root            String authority = getEncodedAuthority();
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (authority == null) {
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Parse out user info and then port.
1115cd6228dd377b2a0caa02a1e6df92f3d9ae702a95Adam Vartanian            int userInfoSeparator = authority.lastIndexOf('@');
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int portSeparator = authority.indexOf(':', userInfoSeparator);
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1118fc01794f33057862a361a0d0113630c58befc21bKenny Root            String encodedHost = portSeparator == NOT_FOUND
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? authority.substring(userInfoSeparator + 1)
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : authority.substring(userInfoSeparator + 1, portSeparator);
1121fc01794f33057862a361a0d0113630c58befc21bKenny Root
1122fc01794f33057862a361a0d0113630c58befc21bKenny Root            return decode(encodedHost);
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile int port = NOT_CALCULATED;
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getPort() {
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return port == NOT_CALCULATED
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? port = parsePort()
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : port;
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int parsePort() {
1134fc01794f33057862a361a0d0113630c58befc21bKenny Root            String authority = getEncodedAuthority();
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (authority == null) {
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return -1;
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Make sure we look for the port separtor *after* the user info
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // separator. We have URLs with a ':' in the user info.
1141cd6228dd377b2a0caa02a1e6df92f3d9ae702a95Adam Vartanian            int userInfoSeparator = authority.lastIndexOf('@');
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int portSeparator = authority.indexOf(':', userInfoSeparator);
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (portSeparator == NOT_FOUND) {
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return -1;
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1148fc01794f33057862a361a0d0113630c58befc21bKenny Root            String portString = decode(authority.substring(portSeparator + 1));
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return Integer.parseInt(portString);
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.w(LOG, "Error parsing port string.", e);
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return -1;
11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Hierarchical Uri.
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class HierarchicalUri extends AbstractHierarchicalUri {
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** Used in parcelling. */
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final int TYPE_ID = 3;
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1166a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru        private final String scheme; // can be null
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Part authority;
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final PathPart path;
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Part query;
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final Part fragment;
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private HierarchicalUri(String scheme, Part authority, PathPart path,
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part query, Part fragment) {
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.scheme = scheme;
1175a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            this.authority = Part.nonNull(authority);
1176a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            this.path = path == null ? PathPart.NULL : path;
1177a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            this.query = Part.nonNull(query);
1178a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            this.fragment = Part.nonNull(fragment);
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Uri readFrom(Parcel parcel) {
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new HierarchicalUri(
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.readString(),
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.readFrom(parcel),
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                PathPart.readFrom(parcel),
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.readFrom(parcel),
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Part.readFrom(parcel)
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            );
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int describeContents() {
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel parcel, int flags) {
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeInt(TYPE_ID);
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parcel.writeString(scheme);
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            authority.writeTo(parcel);
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            path.writeTo(parcel);
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            query.writeTo(parcel);
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fragment.writeTo(parcel);
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isHierarchical() {
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isRelative() {
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return scheme == null;
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getScheme() {
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return scheme;
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part ssp;
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part getSsp() {
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ssp == null
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? ssp = Part.fromEncoded(makeSchemeSpecificPart()) : ssp;
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedSchemeSpecificPart() {
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getSsp().getEncoded();
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getSchemeSpecificPart() {
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return getSsp().getDecoded();
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates the encoded scheme-specific part from its sub parts.
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String makeSchemeSpecificPart() {
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder builder = new StringBuilder();
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            appendSspTo(builder);
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return builder.toString();
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private void appendSspTo(StringBuilder builder) {
1241a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            String encodedAuthority = authority.getEncoded();
1242a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            if (encodedAuthority != null) {
1243a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru                // Even if the authority is "", we still want to append "//".
1244a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru                builder.append("//").append(encodedAuthority);
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String encodedPath = path.getEncoded();
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encodedPath != null) {
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                builder.append(encodedPath);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1252a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            if (!query.isEmpty()) {
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                builder.append('?').append(query.getEncoded());
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getAuthority() {
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.authority.getDecoded();
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedAuthority() {
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.authority.getEncoded();
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedPath() {
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.path.getEncoded();
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getPath() {
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.path.getDecoded();
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getQuery() {
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.query.getDecoded();
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedQuery() {
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.query.getEncoded();
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getFragment() {
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.fragment.getDecoded();
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncodedFragment() {
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.fragment.getEncoded();
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public List<String> getPathSegments() {
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this.path.getPathSegments();
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private volatile String uriString = NOT_CACHED;
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean cached = (uriString != NOT_CACHED);
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cached ? uriString
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : (uriString = makeUriString());
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String makeUriString() {
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuilder builder = new StringBuilder();
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (scheme != null) {
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                builder.append(scheme).append(':');
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            appendSspTo(builder);
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1312a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru            if (!fragment.isEmpty()) {
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                builder.append('#').append(fragment.getEncoded());
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return builder.toString();
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder buildUpon() {
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new Builder()
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .scheme(scheme)
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .authority(authority)
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .path(path)
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .query(query)
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    .fragment(fragment);
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Helper class for building or manipulating URI references. Not safe for
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * concurrent use.
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>An absolute hierarchical URI reference follows the pattern:
13344e8620f868e2490782ebb960404140ea9482c91dBen Dodson     * {@code <scheme>://<authority><absolute path>?<query>#<fragment>}
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Relative URI references (which are always hierarchical) follow one
13374e8620f868e2490782ebb960404140ea9482c91dBen Dodson     * of two patterns: {@code <relative or absolute path>?<query>#<fragment>}
13384e8620f868e2490782ebb960404140ea9482c91dBen Dodson     * or {@code //<authority><absolute path>?<query>#<fragment>}
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>An opaque URI follows this pattern:
13414e8620f868e2490782ebb960404140ea9482c91dBen Dodson     * {@code <scheme>:<opaque part>#<fragment>}
13420f28af209ac877091f4a096f7553f02a0b401596Jesse Wilson     *
134358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     * <p>Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI.
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final class Builder {
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String scheme;
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part opaquePart;
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part authority;
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private PathPart path;
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part query;
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part fragment;
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructs a new Builder.
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder() {}
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the scheme.
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param scheme name or {@code null} if this is a relative Uri
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder scheme(String scheme) {
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.scheme = scheme;
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder opaquePart(Part opaquePart) {
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.opaquePart = opaquePart;
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes and sets the given opaque scheme-specific-part.
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param opaquePart decoded opaque part
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder opaquePart(String opaquePart) {
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return opaquePart(Part.fromDecoded(opaquePart));
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the previously encoded opaque scheme-specific-part.
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param opaquePart encoded opaque part
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder encodedOpaquePart(String opaquePart) {
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return opaquePart(Part.fromEncoded(opaquePart));
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder authority(Part authority) {
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This URI will be hierarchical.
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.opaquePart = null;
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.authority = authority;
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes and sets the authority.
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder authority(String authority) {
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return authority(Part.fromDecoded(authority));
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the previously encoded authority.
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder encodedAuthority(String authority) {
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return authority(Part.fromEncoded(authority));
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder path(PathPart path) {
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This URI will be hierarchical.
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.opaquePart = null;
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.path = path;
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the path. Leaves '/' characters intact but encodes others as
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * necessary.
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <p>If the path is not null and doesn't start with a '/', and if
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * you specify a scheme and/or authority, the builder will prepend the
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * given path with a '/'.
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder path(String path) {
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path(PathPart.fromDecoded(path));
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the previously encoded path.
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <p>If the path is not null and doesn't start with a '/', and if
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * you specify a scheme and/or authority, the builder will prepend the
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * given path with a '/'.
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder encodedPath(String path) {
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path(PathPart.fromEncoded(path));
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes the given segment and appends it to the path.
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder appendPath(String newSegment) {
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path(PathPart.appendDecodedSegment(path, newSegment));
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Appends the given segment to the path.
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder appendEncodedPath(String newSegment) {
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path(PathPart.appendEncodedSegment(path, newSegment));
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder query(Part query) {
14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This URI will be hierarchical.
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.opaquePart = null;
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.query = query;
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes and sets the query.
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder query(String query) {
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return query(Part.fromDecoded(query));
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the previously encoded query.
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder encodedQuery(String query) {
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return query(Part.fromEncoded(query));
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder fragment(Part fragment) {
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.fragment = fragment;
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes and sets the fragment.
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder fragment(String fragment) {
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fragment(Part.fromDecoded(fragment));
14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the previously encoded fragment.
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder encodedFragment(String fragment) {
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fragment(Part.fromEncoded(fragment));
14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Encodes the key and value and then appends the parameter to the
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * query string.
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param key which will be encoded
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param value which will be encoded
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Builder appendQueryParameter(String key, String value) {
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // This URI will be hierarchical.
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.opaquePart = null;
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String encodedParameter = encode(key, null) + "="
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + encode(value, null);
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (query == null) {
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                query = Part.fromEncoded(encodedParameter);
15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return this;
15179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String oldQuery = query.getEncoded();
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldQuery == null || oldQuery.length() == 0) {
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                query = Part.fromEncoded(encodedParameter);
15229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                query = Part.fromEncoded(oldQuery + "&" + encodedParameter);
15249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
153058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson         * Clears the the previously set query.
153158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson         */
153258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        public Builder clearQuery() {
153358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson          return query((Part) null);
153458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        }
153558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
153658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        /**
15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructs a Uri with the current attributes.
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @throws UnsupportedOperationException if the URI is opaque and the
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *  scheme is null
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Uri build() {
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (opaquePart != null) {
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (this.scheme == null) {
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new UnsupportedOperationException(
15469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            "An opaque URI must have a scheme.");
15479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new OpaqueUri(scheme, opaquePart, fragment);
15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Hierarchical URIs should not return null for getPath().
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                PathPart path = this.path;
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (path == null || path == PathPart.NULL) {
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    path = PathPart.EMPTY;
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // If we have a scheme and/or authority, the path must
15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // be absolute. Prepend it with a '/' if necessary.
15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (hasSchemeOrAuthority()) {
15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        path = PathPart.makeAbsolute(path);
15609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
15619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new HierarchicalUri(
15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        scheme, authority, path, query, fragment);
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private boolean hasSchemeOrAuthority() {
15699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return scheme != null
15709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    || (authority != null && authority != Part.NULL);
15719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return build().toString();
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
158158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     * Returns a set of the unique names of all query parameters. Iterating
158258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     * over the set will return the names in order of their first occurrence.
158358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     *
158458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     * @throws UnsupportedOperationException if this isn't a hierarchical URI
158558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     *
158658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     * @return a set of decoded names
158758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson     */
158858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson    public Set<String> getQueryParameterNames() {
158958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        if (isOpaque()) {
159058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
159158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        }
159258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
159358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        String query = getEncodedQuery();
159458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        if (query == null) {
159558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            return Collections.emptySet();
159658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        }
159758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
159858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        Set<String> names = new LinkedHashSet<String>();
159958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        int start = 0;
160058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        do {
160158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int next = query.indexOf('&', start);
160258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int end = (next == -1) ? query.length() : next;
160358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
160458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int separator = query.indexOf('=', start);
160558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (separator > end || separator == -1) {
160658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                separator = end;
160758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            }
160858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
160958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            String name = query.substring(start, separator);
161058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            names.add(decode(name));
161158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
161258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            // Move start to end of name.
161358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            start = end + 1;
161458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        } while (start < query.length());
161558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
161658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        return Collections.unmodifiableSet(names);
161758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson    }
161858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
161958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson    /**
16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Searches the query string for parameter values with the given key.
16219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param key which will be encoded
16239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws UnsupportedOperationException if this isn't a hierarchical URI
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if key is null
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return a list of decoded values
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public List<String> getQueryParameters(String key) {
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isOpaque()) {
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
16319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
163258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        if (key == null) {
163358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson          throw new NullPointerException("key");
163458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        }
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1636a8675f67e33bc7337d148358783b0fd138b501ffJean-Baptiste Queru        String query = getEncodedQuery();
16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (query == null) {
16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return Collections.emptyList();
16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String encodedKey;
16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            encodedKey = URLEncoder.encode(key, DEFAULT_ENCODING);
16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (UnsupportedEncodingException e) {
16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new AssertionError(e);
16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ArrayList<String> values = new ArrayList<String>();
16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = 0;
165158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        do {
165258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int nextAmpersand = query.indexOf('&', start);
165358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int end = nextAmpersand != -1 ? nextAmpersand : query.length();
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
165558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int separator = query.indexOf('=', start);
165658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (separator > end || separator == -1) {
165758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                separator = end;
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
166058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (separator - start == encodedKey.length()
166158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
166258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                if (separator == end) {
166358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                  values.add("");
166458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                } else {
166558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                  values.add(decode(query.substring(separator + 1, end)));
166658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                }
16679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
166958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            // Move start to end of name.
167058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (nextAmpersand != -1) {
167158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                start = nextAmpersand + 1;
167258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            } else {
167358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                break;
167458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            }
167558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        } while (true);
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Collections.unmodifiableList(values);
16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Searches the query string for the first value with the given key.
16829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
16834356c95354d28b8ceb1b04e97c53c2ba3b3faa93Narayan Kamath     * <p><strong>Warning:</strong> Prior to Jelly Bean, this decoded
168441e0839b136cdeccd2f45de1d9c56240f3933a1eJesse Wilson     * the '+' character as '+' rather than ' '.
168541e0839b136cdeccd2f45de1d9c56240f3933a1eJesse Wilson     *
16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param key which will be encoded
16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws UnsupportedOperationException if this isn't a hierarchical URI
16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if key is null
16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the decoded value or null if no parameter is found
16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1691e26450b7142feae9902b432eb01bcd33f3cbd1bdScott Kennedy    @Nullable
16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String getQueryParameter(String key) {
16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isOpaque()) {
16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
169670ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick        if (key == null) {
1697f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton            throw new NullPointerException("key");
169870ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick        }
16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
170070ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick        final String query = getEncodedQuery();
17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (query == null) {
17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
170570ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick        final String encodedKey = encode(key, null);
170658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        final int length = query.length();
170758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        int start = 0;
170858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        do {
170958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int nextAmpersand = query.indexOf('&', start);
171058a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int end = nextAmpersand != -1 ? nextAmpersand : length;
17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
171258a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            int separator = query.indexOf('=', start);
171358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (separator > end || separator == -1) {
171458a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                separator = end;
171570ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick            }
171658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
171758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (separator - start == encodedKey.length()
171858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
171958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                if (separator == end) {
17200f28af209ac877091f4a096f7553f02a0b401596Jesse Wilson                    return "";
172158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                } else {
17220f28af209ac877091f4a096f7553f02a0b401596Jesse Wilson                    String encodedValue = query.substring(separator + 1, end);
1723d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                    return UriCodec.decode(encodedValue, true, StandardCharsets.UTF_8, false);
172470ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick                }
172558a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            }
172658a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson
172758a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            // Move start to end of name.
172858a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson            if (nextAmpersand != -1) {
172958a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                start = nextAmpersand + 1;
1730e41c317348cfe23a78bfd97609c3489c6cc4a786Keith Ito            } else {
173158a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson                break;
17329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
173358a345936d7e2b66bdeefb492e4f777754792d7eBen Dodson        } while (true);
173470ece8d334485af47edfb5bf3049bef2cf5f4df6Brad Fitzpatrick        return null;
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1737f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton    /**
1738f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * Searches the query string for the first value with the given key and interprets it
1739f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
1740f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * else is interpreted as <code>true</code>.
1741f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     *
1742f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * @param key which will be decoded
1743f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * @param defaultValue the default value to return if there is no query parameter for key
1744f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     * @return the boolean interpretation of the query parameter key
1745f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton     */
1746f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton    public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
1747f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton        String flag = getQueryParameter(key);
1748f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton        if (flag == null) {
1749f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton            return defaultValue;
1750f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton        }
1751cb64d430627b71221c588ef5f23599dd34a89ee9Elliott Hughes        flag = flag.toLowerCase(Locale.ROOT);
1752f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton        return (!"false".equals(flag) && !"0".equals(flag));
1753f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton    }
1754f0cfe3438aea77b5193d94fb9fa0c8d37972b194Jeff Hamilton
1755ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly    /**
1756abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson     * Return an equivalent URI with a lowercase scheme component.
1757ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * This aligns the Uri with Android best practices for
1758ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * intent filtering.
1759ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     *
1760ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * <p>For example, "HTTP://www.android.com" becomes
1761ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * "http://www.android.com"
1762ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     *
1763ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * <p>All URIs received from outside Android (such as user input,
1764ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * or external sources like Bluetooth, NFC, or the Internet) should
1765ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * be normalized before they are used to create an Intent.
1766ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     *
1767ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * <p class="note">This method does <em>not</em> validate bad URI's,
1768ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * or 'fix' poorly formatted URI's - so do not use it for input validation.
1769ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * A Uri will always be returned, even if the Uri is badly formatted to
1770ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * begin with and a scheme component cannot be found.
1771ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     *
1772ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     * @return normalized Uri (never null)
177354de77470de4f605eef7f4b4e01718b301fe275eElliot Waite     * @see android.content.Intent#setData
177454de77470de4f605eef7f4b4e01718b301fe275eElliot Waite     * @see android.content.Intent#setDataAndNormalize
1775ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly     */
1776abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson    public Uri normalizeScheme() {
1777ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly        String scheme = getScheme();
1778ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly        if (scheme == null) return this;  // give up
1779cb64d430627b71221c588ef5f23599dd34a89ee9Elliott Hughes        String lowerScheme = scheme.toLowerCase(Locale.ROOT);
1780ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly        if (scheme.equals(lowerScheme)) return this;  // no change
1781ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly
1782ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly        return buildUpon().scheme(lowerScheme).build();
1783ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly    }
1784ccae412deda8b0c165c86f395752c0667a3411a6Nick Pelly
17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Identifies a null parcelled Uri. */
17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int NULL_TYPE_ID = 0;
17879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Reads Uris from Parcels.
17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
17919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final Parcelable.Creator<Uri> CREATOR
17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = new Parcelable.Creator<Uri>() {
17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Uri createFromParcel(Parcel in) {
17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int type = in.readInt();
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (type) {
17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case NULL_TYPE_ID: return null;
17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case StringUri.TYPE_ID: return StringUri.readFrom(in);
17989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case OpaqueUri.TYPE_ID: return OpaqueUri.readFrom(in);
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case HierarchicalUri.TYPE_ID:
18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return HierarchicalUri.readFrom(in);
18019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18038e8d65ff5fdef12c6af3d003dfef19aadc39bea9Dianne Hackborn            throw new IllegalArgumentException("Unknown URI type: " + type);
18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Uri[] newArray(int size) {
18079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new Uri[size];
18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Writes a Uri to a Parcel.
18139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param out parcel to write to
18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param uri to write, can be null
18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void writeToParcel(Parcel out, Uri uri) {
18189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (uri == null) {
18199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.writeInt(NULL_TYPE_ID);
18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            uri.writeToParcel(out, 0);
18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Encodes characters in the given string as '%'-escaped octets
18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
18309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * all other characters.
18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param s string to encode
18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an encoded version of s suitable for use as a URI component,
18359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  or null if s is null
18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String encode(String s) {
18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return encode(s, null);
18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Encodes characters in the given string as '%'-escaped octets
18439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * all other characters with the exception of those specified in the
18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * allow argument.
18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param s string to encode
18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param allow set of additional characters to allow in the encoded form,
18509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  null if no characters should be skipped
18519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an encoded version of s suitable for use as a URI component,
18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  or null if s is null
18539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
18549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String encode(String s, String allow) {
18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s == null) {
18569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
18579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Lazily-initialized buffers.
18609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder encoded = null;
18619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int oldLength = s.length();
18639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // This loop alternates between copying over allowed characters and
18659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // encoding in chunks. This results in fewer method calls and
18669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // allocations than encoding one character at a time.
18679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int current = 0;
18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (current < oldLength) {
18699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Start in "copying" mode where we copy over allowed chars.
18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next character which needs to be encoded.
18729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextToEncode = current;
18739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (nextToEncode < oldLength
18749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && isAllowed(s.charAt(nextToEncode), allow)) {
18759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextToEncode++;
18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If there's nothing more to encode...
18799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (nextToEncode == oldLength) {
18809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (current == 0) {
18819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // We didn't need to encode anything!
18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return s;
18839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
18849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Presumably, we've already done some encoding.
18859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    encoded.append(s, current, oldLength);
18869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return encoded.toString();
18879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encoded == null) {
18919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                encoded = new StringBuilder();
18929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (nextToEncode > current) {
18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Append allowed characters leading up to this point.
18969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                encoded.append(s, current, nextToEncode);
18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
18989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // assert nextToEncode == current
18999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Switch to "encoding" mode.
19029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Find the next allowed character.
19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            current = nextToEncode;
19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nextAllowed = current + 1;
19069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (nextAllowed < oldLength
19079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && !isAllowed(s.charAt(nextAllowed), allow)) {
19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nextAllowed++;
19099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Convert the substring to bytes and encode the bytes as
19129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // '%'-escaped octets.
19139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String toEncode = s.substring(current, nextAllowed);
19149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
19159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                byte[] bytes = toEncode.getBytes(DEFAULT_ENCODING);
19169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int bytesLength = bytes.length;
19179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < bytesLength; i++) {
19189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    encoded.append('%');
19199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    encoded.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4]);
19209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    encoded.append(HEX_DIGITS[bytes[i] & 0xf]);
19219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
19229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (UnsupportedEncodingException e) {
19239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new AssertionError(e);
19249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            current = nextAllowed;
19279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
19289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Encoded could still be null at this point if s is empty.
19309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return encoded == null ? s : encoded.toString();
19319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
19349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if the given character is allowed.
19359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
19369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param c character to check
19379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param allow characters to allow
19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if the character is allowed or false if it should be
19399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  encoded
19409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
19419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean isAllowed(char c, String allow) {
19429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (c >= 'A' && c <= 'Z')
19439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                || (c >= 'a' && c <= 'z')
19449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                || (c >= '0' && c <= '9')
19459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                || "_-!.~'()*".indexOf(c) != NOT_FOUND
19469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                || (allow != null && allow.indexOf(c) != NOT_FOUND);
19479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
19509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Decodes '%'-escaped octets in the given string using the UTF-8 scheme.
19519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Replaces invalid octets with the unicode replacement character
19529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ("\\uFFFD").
19539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
19549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param s encoded string to decode
19559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the given string with escaped octets decoded, or null if
19569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  s is null
19579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
19589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String decode(String s) {
19599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s == null) {
19609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
19619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1962d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes        return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
19639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
19669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Support for part implementations.
19679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
19689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static abstract class AbstractPart {
19699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
19719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Enum which indicates which representation of a given part we have.
19729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
19739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static class Representation {
19749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            static final int BOTH = 0;
19759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            static final int ENCODED = 1;
19769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            static final int DECODED = 2;
19779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
19789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        volatile String encoded;
19809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        volatile String decoded;
19819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        AbstractPart(String encoded, String decoded) {
19839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.encoded = encoded;
19849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.decoded = decoded;
19859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
19869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        abstract String getEncoded();
19889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final String getDecoded() {
19909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
19919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasDecoded = decoded != NOT_CACHED;
19929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return hasDecoded ? decoded : (decoded = decode(encoded));
19939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
19949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final void writeTo(Parcel parcel) {
19969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
19979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasEncoded = encoded != NOT_CACHED;
19989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
20009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasDecoded = decoded != NOT_CACHED;
20019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (hasEncoded && hasDecoded) {
20039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeInt(Representation.BOTH);
20049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeString(encoded);
20059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeString(decoded);
20069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (hasEncoded) {
20079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeInt(Representation.ENCODED);
20089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeString(encoded);
20099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (hasDecoded) {
20109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeInt(Representation.DECODED);
20119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                parcel.writeString(decoded);
20129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
20138e8d65ff5fdef12c6af3d003dfef19aadc39bea9Dianne Hackborn                throw new IllegalArgumentException("Neither encoded nor decoded");
20149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
20159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
20179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
20199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Immutable wrapper of encoded and decoded versions of a URI part. Lazily
20209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * creates the encoded or decoded version from the other.
20219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
20229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static class Part extends AbstractPart {
20239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** A part with null values. */
20259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final Part NULL = new EmptyPart(null);
20269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** A part with empty strings for values. */
20289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final Part EMPTY = new EmptyPart("");
20299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Part(String encoded, String decoded) {
20319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(encoded, decoded);
20329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean isEmpty() {
20359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
20369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String getEncoded() {
20399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
20409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasEncoded = encoded != NOT_CACHED;
20419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return hasEncoded ? encoded : (encoded = encode(decoded));
20429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Part readFrom(Parcel parcel) {
20459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int representation = parcel.readInt();
20469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (representation) {
20479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.BOTH:
20489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return from(parcel.readString(), parcel.readString());
20499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.ENCODED:
20509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return fromEncoded(parcel.readString());
20519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.DECODED:
20529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return fromDecoded(parcel.readString());
20539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                default:
20548e8d65ff5fdef12c6af3d003dfef19aadc39bea9Dianne Hackborn                    throw new IllegalArgumentException("Unknown representation: "
20558e8d65ff5fdef12c6af3d003dfef19aadc39bea9Dianne Hackborn                            + representation);
20569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
20579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
20609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns given part or {@link #NULL} if the given part is null.
20619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
20629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Part nonNull(Part part) {
20639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return part == null ? NULL : part;
20649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
20679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a part from the encoded string.
20689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
20699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param encoded part string
20709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
20719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Part fromEncoded(String encoded) {
20729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return from(encoded, NOT_CACHED);
20739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
20769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a part from the decoded string.
20779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
20789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param decoded part string
20799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
20809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Part fromDecoded(String decoded) {
20819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return from(NOT_CACHED, decoded);
20829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
20839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
20859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a part from the encoded and decoded strings.
20869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
20879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param encoded part string
20889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param decoded part string
20899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
20909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static Part from(String encoded, String decoded) {
20919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We have to check both encoded and decoded in case one is
20929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // NOT_CACHED.
20939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encoded == null) {
20959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return NULL;
20969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
20979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encoded.length() == 0) {
20989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return EMPTY;
20999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (decoded == null) {
21029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return NULL;
21039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (decoded .length() == 0) {
21059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return EMPTY;
21069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new Part(encoded, decoded);
21099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
21109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static class EmptyPart extends Part {
21129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public EmptyPart(String value) {
21139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                super(value, value);
21149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
21179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean isEmpty() {
21189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
21199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
21219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
21229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
21249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Immutable wrapper of encoded and decoded versions of a path part. Lazily
21259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * creates the encoded or decoded version from the other.
21269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
21279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static class PathPart extends AbstractPart {
21289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** A part with null values. */
21309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final PathPart NULL = new PathPart(null, null);
21319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** A part with empty strings for values. */
21339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static final PathPart EMPTY = new PathPart("", "");
21349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private PathPart(String encoded, String decoded) {
21369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(encoded, decoded);
21379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
21389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String getEncoded() {
21409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
21419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean hasEncoded = encoded != NOT_CACHED;
21429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Don't encode '/'.
21449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return hasEncoded ? encoded : (encoded = encode(decoded, "/"));
21459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
21469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
21489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Cached path segments. This doesn't need to be volatile--we don't
21499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * care if other threads see the result.
21509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
21519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private PathSegments pathSegments;
21529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
21549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Gets the individual path segments. Parses them if necessary.
21559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
21569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return parsed path segments or null if this isn't a hierarchical
21579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *  URI
21589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
21599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PathSegments getPathSegments() {
21609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (pathSegments != null) {
21619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return pathSegments;
21629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = getEncoded();
21659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path == null) {
21669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return pathSegments = PathSegments.EMPTY;
21679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            PathSegmentsBuilder segmentBuilder = new PathSegmentsBuilder();
21709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int previous = 0;
21729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int current;
21739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while ((current = path.indexOf('/', previous)) > -1) {
21749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // This check keeps us from adding a segment if the path starts
21759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // '/' and an empty segment for "//".
21769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (previous < current) {
21779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String decodedSegment
21789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            = decode(path.substring(previous, current));
21799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    segmentBuilder.add(decodedSegment);
21809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
21819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                previous = current + 1;
21829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Add in the final path segment.
21859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (previous < path.length()) {
21869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                segmentBuilder.add(decode(path.substring(previous)));
21879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
21889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return pathSegments = segmentBuilder.build();
21909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
21919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart appendEncodedSegment(PathPart oldPart,
21939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String newSegment) {
21949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If there is no old path, should we make the new path relative
21959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // or absolute? I pick absolute.
21969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldPart == null) {
21989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // No old path.
21999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return fromEncoded("/" + newSegment);
22009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String oldPath = oldPart.getEncoded();
22039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldPath == null) {
22059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                oldPath = "";
22069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int oldPathLength = oldPath.length();
22099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String newPath;
22109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldPathLength == 0) {
22119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // No old path.
22129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                newPath = "/" + newSegment;
22139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (oldPath.charAt(oldPathLength - 1) == '/') {
22149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                newPath = oldPath + newSegment;
22159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
22169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                newPath = oldPath + "/" + newSegment;
22179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fromEncoded(newPath);
22209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart appendDecodedSegment(PathPart oldPart, String decoded) {
22239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String encoded = encode(decoded);
22249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // TODO: Should we reuse old PathSegments? Probably not.
22269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return appendEncodedSegment(oldPart, encoded);
22279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart readFrom(Parcel parcel) {
22309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int representation = parcel.readInt();
22319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (representation) {
22329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.BOTH:
22339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return from(parcel.readString(), parcel.readString());
22349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.ENCODED:
22359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return fromEncoded(parcel.readString());
22369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Representation.DECODED:
22379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return fromDecoded(parcel.readString());
22389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                default:
22398e8d65ff5fdef12c6af3d003dfef19aadc39bea9Dianne Hackborn                    throw new IllegalArgumentException("Bad representation: " + representation);
22409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
22449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a path from the encoded string.
22459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
22469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param encoded part string
22479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
22489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart fromEncoded(String encoded) {
22499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return from(encoded, NOT_CACHED);
22509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
22539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a path from the decoded string.
22549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
22559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param decoded part string
22569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
22579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart fromDecoded(String decoded) {
22589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return from(NOT_CACHED, decoded);
22599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
22629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Creates a path from the encoded and decoded strings.
22639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
22649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param encoded part string
22659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param decoded part string
22669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
22679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart from(String encoded, String decoded) {
22689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encoded == null) {
22699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return NULL;
22709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (encoded.length() == 0) {
22739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return EMPTY;
22749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new PathPart(encoded, decoded);
22779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
22789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
22809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Prepends path values with "/" if they're present, not empty, and
22819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * they don't already start with "/".
22829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
22839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        static PathPart makeAbsolute(PathPart oldPart) {
22849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
22859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean encodedCached = oldPart.encoded != NOT_CACHED;
22869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We don't care which version we use, and we don't want to force
22889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // unneccessary encoding/decoding.
22899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String oldPath = encodedCached ? oldPart.encoded : oldPart.decoded;
22909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (oldPath == null || oldPath.length() == 0
22929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    || oldPath.startsWith("/")) {
22939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return oldPart;
22949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
22959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
22969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Prepend encoded string if present.
22979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String newEncoded = encodedCached
22989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? "/" + oldPart.encoded : NOT_CACHED;
22999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Prepend decoded string if present.
23019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @SuppressWarnings("StringEquality")
23029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean decodedCached = oldPart.decoded != NOT_CACHED;
23039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String newDecoded = decodedCached
23049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ? "/" + oldPart.decoded
23059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    : NOT_CACHED;
23069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new PathPart(newEncoded, newDecoded);
23089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
23099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
23109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
23119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
23129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a new Uri by appending an already-encoded path segment to a
23139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * base Uri.
23149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
23159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param baseUri Uri to append path segment to
23169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param pathSegment encoded path segment to append
23170f28af209ac877091f4a096f7553f02a0b401596Jesse Wilson     * @return a new Uri based on baseUri with the given segment appended to
23189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *  the path
23199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if baseUri is null
23209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
23219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Uri withAppendedPath(Uri baseUri, String pathSegment) {
23229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Builder builder = baseUri.buildUpon();
23239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        builder = builder.appendEncodedPath(pathSegment);
23249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return builder.build();
23259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
232665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey
232765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey    /**
232865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     * If this {@link Uri} is {@code file://}, then resolve and return its
232965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     * canonical path. Also fixes legacy emulated storage paths so they are
233065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     * usable across user boundaries. Should always be called from the app
233165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     * process before sending elsewhere.
233265c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     *
233365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     * @hide
233465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey     */
233565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey    public Uri getCanonicalUri() {
233665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey        if ("file".equals(getScheme())) {
233765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            final String canonicalPath;
233865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            try {
233965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                canonicalPath = new File(getPath()).getCanonicalPath();
234065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            } catch (IOException e) {
234165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                return this;
234265c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            }
234365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey
234465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            if (Environment.isExternalStorageEmulated()) {
234565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                final String legacyPath = Environment.getLegacyExternalStorageDirectory()
234665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                        .toString();
234765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey
234865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                // Splice in user-specific path when legacy path is found
234965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                if (canonicalPath.startsWith(legacyPath)) {
235065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                    return Uri.fromFile(new File(
235165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                            Environment.getExternalStorageDirectory().toString(),
235265c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                            canonicalPath.substring(legacyPath.length() + 1)));
235365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey                }
235465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            }
235565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey
235665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            return Uri.fromFile(new File(canonicalPath));
235765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey        } else {
235865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey            return this;
235965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey        }
236065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey    }
2361a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey
2362a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey    /**
2363a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey     * If this is a {@code file://} Uri, it will be reported to
2364a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey     * {@link StrictMode}.
2365a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey     *
2366a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey     * @hide
2367a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey     */
2368a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey    public void checkFileUriExposed(String location) {
2369fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey        if ("file".equals(getScheme())
2370fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey                && (getPath() != null) && !getPath().startsWith("/system/")) {
2371344744b49633a7f8aa0c8e43469bbfcf9d8e2cabJeff Sharkey            StrictMode.onFileUriExposed(this, location);
2372a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey        }
2373a14acd20b8d563319ea1a5974dca0e9a29f0aaefJeff Sharkey    }
2374846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
2375846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey    /**
2376fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey     * If this is a {@code content://} Uri without access flags, it will be
2377fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey     * reported to {@link StrictMode}.
2378fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey     *
2379fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey     * @hide
2380fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey     */
2381fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey    public void checkContentUriWithoutPermission(String location, int flags) {
2382fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey        if ("content".equals(getScheme()) && !Intent.isAccessUriMode(flags)) {
2383fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey            StrictMode.onContentUriWithoutPermission(this, location);
2384fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey        }
2385fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey    }
2386fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey
2387fb833f38a0c9a27b55784f2bf3dcd7aa7d652157Jeff Sharkey    /**
2388846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey     * Test if this is a path prefix match against the given Uri. Verifies that
2389846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey     * scheme, authority, and atomic path segments match.
2390846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey     *
2391846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey     * @hide
2392846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey     */
2393846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey    public boolean isPathPrefixMatch(Uri prefix) {
2394846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        if (!Objects.equals(getScheme(), prefix.getScheme())) return false;
2395846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        if (!Objects.equals(getAuthority(), prefix.getAuthority())) return false;
2396846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
2397846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        List<String> seg = getPathSegments();
2398846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        List<String> prefixSeg = prefix.getPathSegments();
2399846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
2400846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        final int prefixSize = prefixSeg.size();
2401846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        if (seg.size() < prefixSize) return false;
2402846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
2403846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        for (int i = 0; i < prefixSize; i++) {
2404846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey            if (!Objects.equals(seg.get(i), prefixSeg.get(i))) {
2405846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey                return false;
2406846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey            }
2407846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        }
2408846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey
2409846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey        return true;
2410846318a3250fa95f47a9decfbffb05a31dbd0006Jeff Sharkey    }
24119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
2412