Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.net;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.util.Log;
     22 
     23 import java.io.File;
     24 import java.io.IOException;
     25 import java.io.UnsupportedEncodingException;
     26 import java.io.ByteArrayOutputStream;
     27 import java.net.URLEncoder;
     28 import java.util.AbstractList;
     29 import java.util.ArrayList;
     30 import java.util.Collections;
     31 import java.util.List;
     32 import java.util.RandomAccess;
     33 
     34 /**
     35  * Immutable URI reference. A URI reference includes a URI and a fragment, the
     36  * component of the URI following a '#'. Builds and parses URI references
     37  * which conform to
     38  * <a href="http://www.faqs.org/rfcs/rfc2396.html">RFC 2396</a>.
     39  *
     40  * <p>In the interest of performance, this class performs little to no
     41  * validation. Behavior is undefined for invalid input. This class is very
     42  * forgiving--in the face of invalid input, it will return garbage
     43  * rather than throw an exception unless otherwise specified.
     44  */
     45 public abstract class Uri implements Parcelable, Comparable<Uri> {
     46 
     47     /*
     48 
     49     This class aims to do as little up front work as possible. To accomplish
     50     that, we vary the implementation dependending on what the user passes in.
     51     For example, we have one implementation if the user passes in a
     52     URI string (StringUri) and another if the user passes in the
     53     individual components (OpaqueUri).
     54 
     55     *Concurrency notes*: Like any truly immutable object, this class is safe
     56     for concurrent use. This class uses a caching pattern in some places where
     57     it doesn't use volatile or synchronized. This is safe to do with ints
     58     because getting or setting an int is atomic. It's safe to do with a String
     59     because the internal fields are final and the memory model guarantees other
     60     threads won't see a partially initialized instance. We are not guaranteed
     61     that some threads will immediately see changes from other threads on
     62     certain platforms, but we don't mind if those threads reconstruct the
     63     cached result. As a result, we get thread safe caching with no concurrency
     64     overhead, which means the most common case, access from a single thread,
     65     is as fast as possible.
     66 
     67     From the Java Language spec.:
     68 
     69     "17.5 Final Field Semantics
     70 
     71     ... when the object is seen by another thread, that thread will always
     72     see the correctly constructed version of that object's final fields.
     73     It will also see versions of any object or array referenced by
     74     those final fields that are at least as up-to-date as the final fields
     75     are."
     76 
     77     In that same vein, all non-transient fields within Uri
     78     implementations should be final and immutable so as to ensure true
     79     immutability for clients even when they don't use proper concurrency
     80     control.
     81 
     82     For reference, from RFC 2396:
     83 
     84     "4.3. Parsing a URI Reference
     85 
     86        A URI reference is typically parsed according to the four main
     87        components and fragment identifier in order to determine what
     88        components are present and whether the reference is relative or
     89        absolute.  The individual components are then parsed for their
     90        subparts and, if not opaque, to verify their validity.
     91 
     92        Although the BNF defines what is allowed in each component, it is
     93        ambiguous in terms of differentiating between an authority component
     94        and a path component that begins with two slash characters.  The
     95        greedy algorithm is used for disambiguation: the left-most matching
     96        rule soaks up as much of the URI reference string as it is capable of
     97        matching.  In other words, the authority component wins."
     98 
     99     The "four main components" of a hierarchical URI consist of
    100     <scheme>://<authority><path>?<query>
    101 
    102     */
    103 
    104     /** Log tag. */
    105     private static final String LOG = Uri.class.getSimpleName();
    106 
    107     /**
    108      * NOTE: EMPTY accesses this field during its own initialization, so this
    109      * field *must* be initialized first, or else EMPTY will see a null value!
    110      *
    111      * Placeholder for strings which haven't been cached. This enables us
    112      * to cache null. We intentionally create a new String instance so we can
    113      * compare its identity and there is no chance we will confuse it with
    114      * user data.
    115      */
    116     @SuppressWarnings("RedundantStringConstructorCall")
    117     private static final String NOT_CACHED = new String("NOT CACHED");
    118 
    119     /**
    120      * The empty URI, equivalent to "".
    121      */
    122     public static final Uri EMPTY = new HierarchicalUri(null, Part.NULL,
    123             PathPart.EMPTY, Part.NULL, Part.NULL);
    124 
    125     /**
    126      * Prevents external subclassing.
    127      */
    128     private Uri() {}
    129 
    130     /**
    131      * Returns true if this URI is hierarchical like "http://google.com".
    132      * Absolute URIs are hierarchical if the scheme-specific part starts with
    133      * a '/'. Relative URIs are always hierarchical.
    134      */
    135     public abstract boolean isHierarchical();
    136 
    137     /**
    138      * Returns true if this URI is opaque like "mailto:nobody (at) google.com". The
    139      * scheme-specific part of an opaque URI cannot start with a '/'.
    140      */
    141     public boolean isOpaque() {
    142         return !isHierarchical();
    143     }
    144 
    145     /**
    146      * Returns true if this URI is relative, i.e. if it doesn't contain an
    147      * explicit scheme.
    148      *
    149      * @return true if this URI is relative, false if it's absolute
    150      */
    151     public abstract boolean isRelative();
    152 
    153     /**
    154      * Returns true if this URI is absolute, i.e. if it contains an
    155      * explicit scheme.
    156      *
    157      * @return true if this URI is absolute, false if it's relative
    158      */
    159     public boolean isAbsolute() {
    160         return !isRelative();
    161     }
    162 
    163     /**
    164      * Gets the scheme of this URI. Example: "http"
    165      *
    166      * @return the scheme or null if this is a relative URI
    167      */
    168     public abstract String getScheme();
    169 
    170     /**
    171      * Gets the scheme-specific part of this URI, i.e. everything between the
    172      * scheme separator ':' and the fragment separator '#'. If this is a
    173      * relative URI, this method returns the entire URI. Decodes escaped octets.
    174      *
    175      * <p>Example: "//www.google.com/search?q=android"
    176      *
    177      * @return the decoded scheme-specific-part
    178      */
    179     public abstract String getSchemeSpecificPart();
    180 
    181     /**
    182      * Gets the scheme-specific part of this URI, i.e. everything between the
    183      * scheme separator ':' and the fragment separator '#'. If this is a
    184      * relative URI, this method returns the entire URI. Leaves escaped octets
    185      * intact.
    186      *
    187      * <p>Example: "//www.google.com/search?q=android"
    188      *
    189      * @return the decoded scheme-specific-part
    190      */
    191     public abstract String getEncodedSchemeSpecificPart();
    192 
    193     /**
    194      * Gets the decoded authority part of this URI. For
    195      * server addresses, the authority is structured as follows:
    196      * {@code [ userinfo '@' ] host [ ':' port ]}
    197      *
    198      * <p>Examples: "google.com", "bob (at) google.com:80"
    199      *
    200      * @return the authority for this URI or null if not present
    201      */
    202     public abstract String getAuthority();
    203 
    204     /**
    205      * Gets the encoded authority part of this URI. For
    206      * server addresses, the authority is structured as follows:
    207      * {@code [ userinfo '@' ] host [ ':' port ]}
    208      *
    209      * <p>Examples: "google.com", "bob (at) google.com:80"
    210      *
    211      * @return the authority for this URI or null if not present
    212      */
    213     public abstract String getEncodedAuthority();
    214 
    215     /**
    216      * Gets the decoded user information from the authority.
    217      * For example, if the authority is "nobody (at) google.com", this method will
    218      * return "nobody".
    219      *
    220      * @return the user info for this URI or null if not present
    221      */
    222     public abstract String getUserInfo();
    223 
    224     /**
    225      * Gets the encoded user information from the authority.
    226      * For example, if the authority is "nobody (at) google.com", this method will
    227      * return "nobody".
    228      *
    229      * @return the user info for this URI or null if not present
    230      */
    231     public abstract String getEncodedUserInfo();
    232 
    233     /**
    234      * Gets the encoded host from the authority for this URI. For example,
    235      * if the authority is "bob (at) google.com", this method will return
    236      * "google.com".
    237      *
    238      * @return the host for this URI or null if not present
    239      */
    240     public abstract String getHost();
    241 
    242     /**
    243      * Gets the port from the authority for this URI. For example,
    244      * if the authority is "google.com:80", this method will return 80.
    245      *
    246      * @return the port for this URI or -1 if invalid or not present
    247      */
    248     public abstract int getPort();
    249 
    250     /**
    251      * Gets the decoded path.
    252      *
    253      * @return the decoded path, or null if this is not a hierarchical URI
    254      * (like "mailto:nobody (at) google.com") or the URI is invalid
    255      */
    256     public abstract String getPath();
    257 
    258     /**
    259      * Gets the encoded path.
    260      *
    261      * @return the encoded path, or null if this is not a hierarchical URI
    262      * (like "mailto:nobody (at) google.com") or the URI is invalid
    263      */
    264     public abstract String getEncodedPath();
    265 
    266     /**
    267      * Gets the decoded query component from this URI. The query comes after
    268      * the query separator ('?') and before the fragment separator ('#'). This
    269      * method would return "q=android" for
    270      * "http://www.google.com/search?q=android".
    271      *
    272      * @return the decoded query or null if there isn't one
    273      */
    274     public abstract String getQuery();
    275 
    276     /**
    277      * Gets the encoded query component from this URI. The query comes after
    278      * the query separator ('?') and before the fragment separator ('#'). This
    279      * method would return "q=android" for
    280      * "http://www.google.com/search?q=android".
    281      *
    282      * @return the encoded query or null if there isn't one
    283      */
    284     public abstract String getEncodedQuery();
    285 
    286     /**
    287      * Gets the decoded fragment part of this URI, everything after the '#'.
    288      *
    289      * @return the decoded fragment or null if there isn't one
    290      */
    291     public abstract String getFragment();
    292 
    293     /**
    294      * Gets the encoded fragment part of this URI, everything after the '#'.
    295      *
    296      * @return the encoded fragment or null if there isn't one
    297      */
    298     public abstract String getEncodedFragment();
    299 
    300     /**
    301      * Gets the decoded path segments.
    302      *
    303      * @return decoded path segments, each without a leading or trailing '/'
    304      */
    305     public abstract List<String> getPathSegments();
    306 
    307     /**
    308      * Gets the decoded last segment in the path.
    309      *
    310      * @return the decoded last segment or null if the path is empty
    311      */
    312     public abstract String getLastPathSegment();
    313 
    314     /**
    315      * Compares this Uri to another object for equality. Returns true if the
    316      * encoded string representations of this Uri and the given Uri are
    317      * equal. Case counts. Paths are not normalized. If one Uri specifies a
    318      * default port explicitly and the other leaves it implicit, they will not
    319      * be considered equal.
    320      */
    321     public boolean equals(Object o) {
    322         if (!(o instanceof Uri)) {
    323             return false;
    324         }
    325 
    326         Uri other = (Uri) o;
    327 
    328         return toString().equals(other.toString());
    329     }
    330 
    331     /**
    332      * Hashes the encoded string represention of this Uri consistently with
    333      * {@link #equals(Object)}.
    334      */
    335     public int hashCode() {
    336         return toString().hashCode();
    337     }
    338 
    339     /**
    340      * Compares the string representation of this Uri with that of
    341      * another.
    342      */
    343     public int compareTo(Uri other) {
    344         return toString().compareTo(other.toString());
    345     }
    346 
    347     /**
    348      * Returns the encoded string representation of this URI.
    349      * Example: "http://google.com/"
    350      */
    351     public abstract String toString();
    352 
    353     /**
    354      * Constructs a new builder, copying the attributes from this Uri.
    355      */
    356     public abstract Builder buildUpon();
    357 
    358     /** Index of a component which was not found. */
    359     private final static int NOT_FOUND = -1;
    360 
    361     /** Placeholder value for an index which hasn't been calculated yet. */
    362     private final static int NOT_CALCULATED = -2;
    363 
    364     /**
    365      * Error message presented when a user tries to treat an opaque URI as
    366      * hierarchical.
    367      */
    368     private static final String NOT_HIERARCHICAL
    369             = "This isn't a hierarchical URI.";
    370 
    371     /** Default encoding. */
    372     private static final String DEFAULT_ENCODING = "UTF-8";
    373 
    374     /**
    375      * Creates a Uri which parses the given encoded URI string.
    376      *
    377      * @param uriString an RFC 2396-compliant, encoded URI
    378      * @throws NullPointerException if uriString is null
    379      * @return Uri for this given uri string
    380      */
    381     public static Uri parse(String uriString) {
    382         return new StringUri(uriString);
    383     }
    384 
    385     /**
    386      * Creates a Uri from a file. The URI has the form
    387      * "file://<absolute path>". Encodes path characters with the exception of
    388      * '/'.
    389      *
    390      * <p>Example: "file:///tmp/android.txt"
    391      *
    392      * @throws NullPointerException if file is null
    393      * @return a Uri for the given file
    394      */
    395     public static Uri fromFile(File file) {
    396         if (file == null) {
    397             throw new NullPointerException("file");
    398         }
    399 
    400         PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
    401         return new HierarchicalUri(
    402                 "file", Part.EMPTY, path, Part.NULL, Part.NULL);
    403     }
    404 
    405     /**
    406      * An implementation which wraps a String URI. This URI can be opaque or
    407      * hierarchical, but we extend AbstractHierarchicalUri in case we need
    408      * the hierarchical functionality.
    409      */
    410     private static class StringUri extends AbstractHierarchicalUri {
    411 
    412         /** Used in parcelling. */
    413         static final int TYPE_ID = 1;
    414 
    415         /** URI string representation. */
    416         private final String uriString;
    417 
    418         private StringUri(String uriString) {
    419             if (uriString == null) {
    420                 throw new NullPointerException("uriString");
    421             }
    422 
    423             this.uriString = uriString;
    424         }
    425 
    426         static Uri readFrom(Parcel parcel) {
    427             return new StringUri(parcel.readString());
    428         }
    429 
    430         public int describeContents() {
    431             return 0;
    432         }
    433 
    434         public void writeToParcel(Parcel parcel, int flags) {
    435             parcel.writeInt(TYPE_ID);
    436             parcel.writeString(uriString);
    437         }
    438 
    439         /** Cached scheme separator index. */
    440         private volatile int cachedSsi = NOT_CALCULATED;
    441 
    442         /** Finds the first ':'. Returns -1 if none found. */
    443         private int findSchemeSeparator() {
    444             return cachedSsi == NOT_CALCULATED
    445                     ? cachedSsi = uriString.indexOf(':')
    446                     : cachedSsi;
    447         }
    448 
    449         /** Cached fragment separator index. */
    450         private volatile int cachedFsi = NOT_CALCULATED;
    451 
    452         /** Finds the first '#'. Returns -1 if none found. */
    453         private int findFragmentSeparator() {
    454             return cachedFsi == NOT_CALCULATED
    455                     ? cachedFsi = uriString.indexOf('#', findSchemeSeparator())
    456                     : cachedFsi;
    457         }
    458 
    459         public boolean isHierarchical() {
    460             int ssi = findSchemeSeparator();
    461 
    462             if (ssi == NOT_FOUND) {
    463                 // All relative URIs are hierarchical.
    464                 return true;
    465             }
    466 
    467             if (uriString.length() == ssi + 1) {
    468                 // No ssp.
    469                 return false;
    470             }
    471 
    472             // If the ssp starts with a '/', this is hierarchical.
    473             return uriString.charAt(ssi + 1) == '/';
    474         }
    475 
    476         public boolean isRelative() {
    477             // Note: We return true if the index is 0
    478             return findSchemeSeparator() == NOT_FOUND;
    479         }
    480 
    481         private volatile String scheme = NOT_CACHED;
    482 
    483         public String getScheme() {
    484             @SuppressWarnings("StringEquality")
    485             boolean cached = (scheme != NOT_CACHED);
    486             return cached ? scheme : (scheme = parseScheme());
    487         }
    488 
    489         private String parseScheme() {
    490             int ssi = findSchemeSeparator();
    491             return ssi == NOT_FOUND ? null : uriString.substring(0, ssi);
    492         }
    493 
    494         private Part ssp;
    495 
    496         private Part getSsp() {
    497             return ssp == null ? ssp = Part.fromEncoded(parseSsp()) : ssp;
    498         }
    499 
    500         public String getEncodedSchemeSpecificPart() {
    501             return getSsp().getEncoded();
    502         }
    503 
    504         public String getSchemeSpecificPart() {
    505             return getSsp().getDecoded();
    506         }
    507 
    508         private String parseSsp() {
    509             int ssi = findSchemeSeparator();
    510             int fsi = findFragmentSeparator();
    511 
    512             // Return everything between ssi and fsi.
    513             return fsi == NOT_FOUND
    514                     ? uriString.substring(ssi + 1)
    515                     : uriString.substring(ssi + 1, fsi);
    516         }
    517 
    518         private Part authority;
    519 
    520         private Part getAuthorityPart() {
    521             if (authority == null) {
    522                 String encodedAuthority
    523                         = parseAuthority(this.uriString, findSchemeSeparator());
    524                 return authority = Part.fromEncoded(encodedAuthority);
    525             }
    526 
    527             return authority;
    528         }
    529 
    530         public String getEncodedAuthority() {
    531             return getAuthorityPart().getEncoded();
    532         }
    533 
    534         public String getAuthority() {
    535             return getAuthorityPart().getDecoded();
    536         }
    537 
    538         private PathPart path;
    539 
    540         private PathPart getPathPart() {
    541             return path == null
    542                     ? path = PathPart.fromEncoded(parsePath())
    543                     : path;
    544         }
    545 
    546         public String getPath() {
    547             return getPathPart().getDecoded();
    548         }
    549 
    550         public String getEncodedPath() {
    551             return getPathPart().getEncoded();
    552         }
    553 
    554         public List<String> getPathSegments() {
    555             return getPathPart().getPathSegments();
    556         }
    557 
    558         private String parsePath() {
    559             String uriString = this.uriString;
    560             int ssi = findSchemeSeparator();
    561 
    562             // If the URI is absolute.
    563             if (ssi > -1) {
    564                 // Is there anything after the ':'?
    565                 boolean schemeOnly = ssi + 1 == uriString.length();
    566                 if (schemeOnly) {
    567                     // Opaque URI.
    568                     return null;
    569                 }
    570 
    571                 // A '/' after the ':' means this is hierarchical.
    572                 if (uriString.charAt(ssi + 1) != '/') {
    573                     // Opaque URI.
    574                     return null;
    575                 }
    576             } else {
    577                 // All relative URIs are hierarchical.
    578             }
    579 
    580             return parsePath(uriString, ssi);
    581         }
    582 
    583         private Part query;
    584 
    585         private Part getQueryPart() {
    586             return query == null
    587                     ? query = Part.fromEncoded(parseQuery()) : query;
    588         }
    589 
    590         public String getEncodedQuery() {
    591             return getQueryPart().getEncoded();
    592         }
    593 
    594         private String parseQuery() {
    595             // It doesn't make sense to cache this index. We only ever
    596             // calculate it once.
    597             int qsi = uriString.indexOf('?', findSchemeSeparator());
    598             if (qsi == NOT_FOUND) {
    599                 return null;
    600             }
    601 
    602             int fsi = findFragmentSeparator();
    603 
    604             if (fsi == NOT_FOUND) {
    605                 return uriString.substring(qsi + 1);
    606             }
    607 
    608             if (fsi < qsi) {
    609                 // Invalid.
    610                 return null;
    611             }
    612 
    613             return uriString.substring(qsi + 1, fsi);
    614         }
    615 
    616         public String getQuery() {
    617             return getQueryPart().getDecoded();
    618         }
    619 
    620         private Part fragment;
    621 
    622         private Part getFragmentPart() {
    623             return fragment == null
    624                     ? fragment = Part.fromEncoded(parseFragment()) : fragment;
    625         }
    626 
    627         public String getEncodedFragment() {
    628             return getFragmentPart().getEncoded();
    629         }
    630 
    631         private String parseFragment() {
    632             int fsi = findFragmentSeparator();
    633             return fsi == NOT_FOUND ? null : uriString.substring(fsi + 1);
    634         }
    635 
    636         public String getFragment() {
    637             return getFragmentPart().getDecoded();
    638         }
    639 
    640         public String toString() {
    641             return uriString;
    642         }
    643 
    644         /**
    645          * Parses an authority out of the given URI string.
    646          *
    647          * @param uriString URI string
    648          * @param ssi scheme separator index, -1 for a relative URI
    649          *
    650          * @return the authority or null if none is found
    651          */
    652         static String parseAuthority(String uriString, int ssi) {
    653             int length = uriString.length();
    654 
    655             // If "//" follows the scheme separator, we have an authority.
    656             if (length > ssi + 2
    657                     && uriString.charAt(ssi + 1) == '/'
    658                     && uriString.charAt(ssi + 2) == '/') {
    659                 // We have an authority.
    660 
    661                 // Look for the start of the path, query, or fragment, or the
    662                 // end of the string.
    663                 int end = ssi + 3;
    664                 LOOP: while (end < length) {
    665                     switch (uriString.charAt(end)) {
    666                         case '/': // Start of path
    667                         case '?': // Start of query
    668                         case '#': // Start of fragment
    669                             break LOOP;
    670                     }
    671                     end++;
    672                 }
    673 
    674                 return uriString.substring(ssi + 3, end);
    675             } else {
    676                 return null;
    677             }
    678 
    679         }
    680 
    681         /**
    682          * Parses a path out of this given URI string.
    683          *
    684          * @param uriString URI string
    685          * @param ssi scheme separator index, -1 for a relative URI
    686          *
    687          * @return the path
    688          */
    689         static String parsePath(String uriString, int ssi) {
    690             int length = uriString.length();
    691 
    692             // Find start of path.
    693             int pathStart;
    694             if (length > ssi + 2
    695                     && uriString.charAt(ssi + 1) == '/'
    696                     && uriString.charAt(ssi + 2) == '/') {
    697                 // Skip over authority to path.
    698                 pathStart = ssi + 3;
    699                 LOOP: while (pathStart < length) {
    700                     switch (uriString.charAt(pathStart)) {
    701                         case '?': // Start of query
    702                         case '#': // Start of fragment
    703                             return ""; // Empty path.
    704                         case '/': // Start of path!
    705                             break LOOP;
    706                     }
    707                     pathStart++;
    708                 }
    709             } else {
    710                 // Path starts immediately after scheme separator.
    711                 pathStart = ssi + 1;
    712             }
    713 
    714             // Find end of path.
    715             int pathEnd = pathStart;
    716             LOOP: while (pathEnd < length) {
    717                 switch (uriString.charAt(pathEnd)) {
    718                     case '?': // Start of query
    719                     case '#': // Start of fragment
    720                         break LOOP;
    721                 }
    722                 pathEnd++;
    723             }
    724 
    725             return uriString.substring(pathStart, pathEnd);
    726         }
    727 
    728         public Builder buildUpon() {
    729             if (isHierarchical()) {
    730                 return new Builder()
    731                         .scheme(getScheme())
    732                         .authority(getAuthorityPart())
    733                         .path(getPathPart())
    734                         .query(getQueryPart())
    735                         .fragment(getFragmentPart());
    736             } else {
    737                 return new Builder()
    738                         .scheme(getScheme())
    739                         .opaquePart(getSsp())
    740                         .fragment(getFragmentPart());
    741             }
    742         }
    743     }
    744 
    745     /**
    746      * Creates an opaque Uri from the given components. Encodes the ssp
    747      * which means this method cannot be used to create hierarchical URIs.
    748      *
    749      * @param scheme of the URI
    750      * @param ssp scheme-specific-part, everything between the
    751      *  scheme separator (':') and the fragment separator ('#'), which will
    752      *  get encoded
    753      * @param fragment fragment, everything after the '#', null if undefined,
    754      *  will get encoded
    755      *
    756      * @throws NullPointerException if scheme or ssp is null
    757      * @return Uri composed of the given scheme, ssp, and fragment
    758      *
    759      * @see Builder if you don't want the ssp and fragment to be encoded
    760      */
    761     public static Uri fromParts(String scheme, String ssp,
    762             String fragment) {
    763         if (scheme == null) {
    764             throw new NullPointerException("scheme");
    765         }
    766         if (ssp == null) {
    767             throw new NullPointerException("ssp");
    768         }
    769 
    770         return new OpaqueUri(scheme, Part.fromDecoded(ssp),
    771                 Part.fromDecoded(fragment));
    772     }
    773 
    774     /**
    775      * Opaque URI.
    776      */
    777     private static class OpaqueUri extends Uri {
    778 
    779         /** Used in parcelling. */
    780         static final int TYPE_ID = 2;
    781 
    782         private final String scheme;
    783         private final Part ssp;
    784         private final Part fragment;
    785 
    786         private OpaqueUri(String scheme, Part ssp, Part fragment) {
    787             this.scheme = scheme;
    788             this.ssp = ssp;
    789             this.fragment = fragment == null ? Part.NULL : fragment;
    790         }
    791 
    792         static Uri readFrom(Parcel parcel) {
    793             return new OpaqueUri(
    794                 parcel.readString(),
    795                 Part.readFrom(parcel),
    796                 Part.readFrom(parcel)
    797             );
    798         }
    799 
    800         public int describeContents() {
    801             return 0;
    802         }
    803 
    804         public void writeToParcel(Parcel parcel, int flags) {
    805             parcel.writeInt(TYPE_ID);
    806             parcel.writeString(scheme);
    807             ssp.writeTo(parcel);
    808             fragment.writeTo(parcel);
    809         }
    810 
    811         public boolean isHierarchical() {
    812             return false;
    813         }
    814 
    815         public boolean isRelative() {
    816             return scheme == null;
    817         }
    818 
    819         public String getScheme() {
    820             return this.scheme;
    821         }
    822 
    823         public String getEncodedSchemeSpecificPart() {
    824             return ssp.getEncoded();
    825         }
    826 
    827         public String getSchemeSpecificPart() {
    828             return ssp.getDecoded();
    829         }
    830 
    831         public String getAuthority() {
    832             return null;
    833         }
    834 
    835         public String getEncodedAuthority() {
    836             return null;
    837         }
    838 
    839         public String getPath() {
    840             return null;
    841         }
    842 
    843         public String getEncodedPath() {
    844             return null;
    845         }
    846 
    847         public String getQuery() {
    848             return null;
    849         }
    850 
    851         public String getEncodedQuery() {
    852             return null;
    853         }
    854 
    855         public String getFragment() {
    856             return fragment.getDecoded();
    857         }
    858 
    859         public String getEncodedFragment() {
    860             return fragment.getEncoded();
    861         }
    862 
    863         public List<String> getPathSegments() {
    864             return Collections.emptyList();
    865         }
    866 
    867         public String getLastPathSegment() {
    868             return null;
    869         }
    870 
    871         public String getUserInfo() {
    872             return null;
    873         }
    874 
    875         public String getEncodedUserInfo() {
    876             return null;
    877         }
    878 
    879         public String getHost() {
    880             return null;
    881         }
    882 
    883         public int getPort() {
    884             return -1;
    885         }
    886 
    887         private volatile String cachedString = NOT_CACHED;
    888 
    889         public String toString() {
    890             @SuppressWarnings("StringEquality")
    891             boolean cached = cachedString != NOT_CACHED;
    892             if (cached) {
    893                 return cachedString;
    894             }
    895 
    896             StringBuilder sb = new StringBuilder();
    897 
    898             sb.append(scheme).append(':');
    899             sb.append(getEncodedSchemeSpecificPart());
    900 
    901             if (!fragment.isEmpty()) {
    902                 sb.append('#').append(fragment.getEncoded());
    903             }
    904 
    905             return cachedString = sb.toString();
    906         }
    907 
    908         public Builder buildUpon() {
    909             return new Builder()
    910                     .scheme(this.scheme)
    911                     .opaquePart(this.ssp)
    912                     .fragment(this.fragment);
    913         }
    914     }
    915 
    916     /**
    917      * Wrapper for path segment array.
    918      */
    919     static class PathSegments extends AbstractList<String>
    920             implements RandomAccess {
    921 
    922         static final PathSegments EMPTY = new PathSegments(null, 0);
    923 
    924         final String[] segments;
    925         final int size;
    926 
    927         PathSegments(String[] segments, int size) {
    928             this.segments = segments;
    929             this.size = size;
    930         }
    931 
    932         public String get(int index) {
    933             if (index >= size) {
    934                 throw new IndexOutOfBoundsException();
    935             }
    936 
    937             return segments[index];
    938         }
    939 
    940         public int size() {
    941             return this.size;
    942         }
    943     }
    944 
    945     /**
    946      * Builds PathSegments.
    947      */
    948     static class PathSegmentsBuilder {
    949 
    950         String[] segments;
    951         int size = 0;
    952 
    953         void add(String segment) {
    954             if (segments == null) {
    955                 segments = new String[4];
    956             } else if (size + 1 == segments.length) {
    957                 String[] expanded = new String[segments.length * 2];
    958                 System.arraycopy(segments, 0, expanded, 0, segments.length);
    959                 segments = expanded;
    960             }
    961 
    962             segments[size++] = segment;
    963         }
    964 
    965         PathSegments build() {
    966             if (segments == null) {
    967                 return PathSegments.EMPTY;
    968             }
    969 
    970             try {
    971                 return new PathSegments(segments, size);
    972             } finally {
    973                 // Makes sure this doesn't get reused.
    974                 segments = null;
    975             }
    976         }
    977     }
    978 
    979     /**
    980      * Support for hierarchical URIs.
    981      */
    982     private abstract static class AbstractHierarchicalUri extends Uri {
    983 
    984         public String getLastPathSegment() {
    985             // TODO: If we haven't parsed all of the segments already, just
    986             // grab the last one directly so we only allocate one string.
    987 
    988             List<String> segments = getPathSegments();
    989             int size = segments.size();
    990             if (size == 0) {
    991                 return null;
    992             }
    993             return segments.get(size - 1);
    994         }
    995 
    996         private Part userInfo;
    997 
    998         private Part getUserInfoPart() {
    999             return userInfo == null
   1000                     ? userInfo = Part.fromEncoded(parseUserInfo()) : userInfo;
   1001         }
   1002 
   1003         public final String getEncodedUserInfo() {
   1004             return getUserInfoPart().getEncoded();
   1005         }
   1006 
   1007         private String parseUserInfo() {
   1008             String authority = getEncodedAuthority();
   1009             if (authority == null) {
   1010                 return null;
   1011             }
   1012 
   1013             int end = authority.indexOf('@');
   1014             return end == NOT_FOUND ? null : authority.substring(0, end);
   1015         }
   1016 
   1017         public String getUserInfo() {
   1018             return getUserInfoPart().getDecoded();
   1019         }
   1020 
   1021         private volatile String host = NOT_CACHED;
   1022 
   1023         public String getHost() {
   1024             @SuppressWarnings("StringEquality")
   1025             boolean cached = (host != NOT_CACHED);
   1026             return cached ? host
   1027                     : (host = parseHost());
   1028         }
   1029 
   1030         private String parseHost() {
   1031             String authority = getEncodedAuthority();
   1032             if (authority == null) {
   1033                 return null;
   1034             }
   1035 
   1036             // Parse out user info and then port.
   1037             int userInfoSeparator = authority.indexOf('@');
   1038             int portSeparator = authority.indexOf(':', userInfoSeparator);
   1039 
   1040             String encodedHost = portSeparator == NOT_FOUND
   1041                     ? authority.substring(userInfoSeparator + 1)
   1042                     : authority.substring(userInfoSeparator + 1, portSeparator);
   1043 
   1044             return decode(encodedHost);
   1045         }
   1046 
   1047         private volatile int port = NOT_CALCULATED;
   1048 
   1049         public int getPort() {
   1050             return port == NOT_CALCULATED
   1051                     ? port = parsePort()
   1052                     : port;
   1053         }
   1054 
   1055         private int parsePort() {
   1056             String authority = getEncodedAuthority();
   1057             if (authority == null) {
   1058                 return -1;
   1059             }
   1060 
   1061             // Make sure we look for the port separtor *after* the user info
   1062             // separator. We have URLs with a ':' in the user info.
   1063             int userInfoSeparator = authority.indexOf('@');
   1064             int portSeparator = authority.indexOf(':', userInfoSeparator);
   1065 
   1066             if (portSeparator == NOT_FOUND) {
   1067                 return -1;
   1068             }
   1069 
   1070             String portString = decode(authority.substring(portSeparator + 1));
   1071             try {
   1072                 return Integer.parseInt(portString);
   1073             } catch (NumberFormatException e) {
   1074                 Log.w(LOG, "Error parsing port string.", e);
   1075                 return -1;
   1076             }
   1077         }
   1078     }
   1079 
   1080     /**
   1081      * Hierarchical Uri.
   1082      */
   1083     private static class HierarchicalUri extends AbstractHierarchicalUri {
   1084 
   1085         /** Used in parcelling. */
   1086         static final int TYPE_ID = 3;
   1087 
   1088         private final String scheme; // can be null
   1089         private final Part authority;
   1090         private final PathPart path;
   1091         private final Part query;
   1092         private final Part fragment;
   1093 
   1094         private HierarchicalUri(String scheme, Part authority, PathPart path,
   1095                 Part query, Part fragment) {
   1096             this.scheme = scheme;
   1097             this.authority = Part.nonNull(authority);
   1098             this.path = path == null ? PathPart.NULL : path;
   1099             this.query = Part.nonNull(query);
   1100             this.fragment = Part.nonNull(fragment);
   1101         }
   1102 
   1103         static Uri readFrom(Parcel parcel) {
   1104             return new HierarchicalUri(
   1105                 parcel.readString(),
   1106                 Part.readFrom(parcel),
   1107                 PathPart.readFrom(parcel),
   1108                 Part.readFrom(parcel),
   1109                 Part.readFrom(parcel)
   1110             );
   1111         }
   1112 
   1113         public int describeContents() {
   1114             return 0;
   1115         }
   1116 
   1117         public void writeToParcel(Parcel parcel, int flags) {
   1118             parcel.writeInt(TYPE_ID);
   1119             parcel.writeString(scheme);
   1120             authority.writeTo(parcel);
   1121             path.writeTo(parcel);
   1122             query.writeTo(parcel);
   1123             fragment.writeTo(parcel);
   1124         }
   1125 
   1126         public boolean isHierarchical() {
   1127             return true;
   1128         }
   1129 
   1130         public boolean isRelative() {
   1131             return scheme == null;
   1132         }
   1133 
   1134         public String getScheme() {
   1135             return scheme;
   1136         }
   1137 
   1138         private Part ssp;
   1139 
   1140         private Part getSsp() {
   1141             return ssp == null
   1142                     ? ssp = Part.fromEncoded(makeSchemeSpecificPart()) : ssp;
   1143         }
   1144 
   1145         public String getEncodedSchemeSpecificPart() {
   1146             return getSsp().getEncoded();
   1147         }
   1148 
   1149         public String getSchemeSpecificPart() {
   1150             return getSsp().getDecoded();
   1151         }
   1152 
   1153         /**
   1154          * Creates the encoded scheme-specific part from its sub parts.
   1155          */
   1156         private String makeSchemeSpecificPart() {
   1157             StringBuilder builder = new StringBuilder();
   1158             appendSspTo(builder);
   1159             return builder.toString();
   1160         }
   1161 
   1162         private void appendSspTo(StringBuilder builder) {
   1163             String encodedAuthority = authority.getEncoded();
   1164             if (encodedAuthority != null) {
   1165                 // Even if the authority is "", we still want to append "//".
   1166                 builder.append("//").append(encodedAuthority);
   1167             }
   1168 
   1169             String encodedPath = path.getEncoded();
   1170             if (encodedPath != null) {
   1171                 builder.append(encodedPath);
   1172             }
   1173 
   1174             if (!query.isEmpty()) {
   1175                 builder.append('?').append(query.getEncoded());
   1176             }
   1177         }
   1178 
   1179         public String getAuthority() {
   1180             return this.authority.getDecoded();
   1181         }
   1182 
   1183         public String getEncodedAuthority() {
   1184             return this.authority.getEncoded();
   1185         }
   1186 
   1187         public String getEncodedPath() {
   1188             return this.path.getEncoded();
   1189         }
   1190 
   1191         public String getPath() {
   1192             return this.path.getDecoded();
   1193         }
   1194 
   1195         public String getQuery() {
   1196             return this.query.getDecoded();
   1197         }
   1198 
   1199         public String getEncodedQuery() {
   1200             return this.query.getEncoded();
   1201         }
   1202 
   1203         public String getFragment() {
   1204             return this.fragment.getDecoded();
   1205         }
   1206 
   1207         public String getEncodedFragment() {
   1208             return this.fragment.getEncoded();
   1209         }
   1210 
   1211         public List<String> getPathSegments() {
   1212             return this.path.getPathSegments();
   1213         }
   1214 
   1215         private volatile String uriString = NOT_CACHED;
   1216 
   1217         @Override
   1218         public String toString() {
   1219             @SuppressWarnings("StringEquality")
   1220             boolean cached = (uriString != NOT_CACHED);
   1221             return cached ? uriString
   1222                     : (uriString = makeUriString());
   1223         }
   1224 
   1225         private String makeUriString() {
   1226             StringBuilder builder = new StringBuilder();
   1227 
   1228             if (scheme != null) {
   1229                 builder.append(scheme).append(':');
   1230             }
   1231 
   1232             appendSspTo(builder);
   1233 
   1234             if (!fragment.isEmpty()) {
   1235                 builder.append('#').append(fragment.getEncoded());
   1236             }
   1237 
   1238             return builder.toString();
   1239         }
   1240 
   1241         public Builder buildUpon() {
   1242             return new Builder()
   1243                     .scheme(scheme)
   1244                     .authority(authority)
   1245                     .path(path)
   1246                     .query(query)
   1247                     .fragment(fragment);
   1248         }
   1249     }
   1250 
   1251     /**
   1252      * Helper class for building or manipulating URI references. Not safe for
   1253      * concurrent use.
   1254      *
   1255      * <p>An absolute hierarchical URI reference follows the pattern:
   1256      * {@code &lt;scheme&gt;://&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
   1257      *
   1258      * <p>Relative URI references (which are always hierarchical) follow one
   1259      * of two patterns: {@code &lt;relative or absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
   1260      * or {@code //&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
   1261      *
   1262      * <p>An opaque URI follows this pattern:
   1263      * {@code &lt;scheme&gt;:&lt;opaque part&gt;#&lt;fragment&gt;}
   1264      */
   1265     public static final class Builder {
   1266 
   1267         private String scheme;
   1268         private Part opaquePart;
   1269         private Part authority;
   1270         private PathPart path;
   1271         private Part query;
   1272         private Part fragment;
   1273 
   1274         /**
   1275          * Constructs a new Builder.
   1276          */
   1277         public Builder() {}
   1278 
   1279         /**
   1280          * Sets the scheme.
   1281          *
   1282          * @param scheme name or {@code null} if this is a relative Uri
   1283          */
   1284         public Builder scheme(String scheme) {
   1285             this.scheme = scheme;
   1286             return this;
   1287         }
   1288 
   1289         Builder opaquePart(Part opaquePart) {
   1290             this.opaquePart = opaquePart;
   1291             return this;
   1292         }
   1293 
   1294         /**
   1295          * Encodes and sets the given opaque scheme-specific-part.
   1296          *
   1297          * @param opaquePart decoded opaque part
   1298          */
   1299         public Builder opaquePart(String opaquePart) {
   1300             return opaquePart(Part.fromDecoded(opaquePart));
   1301         }
   1302 
   1303         /**
   1304          * Sets the previously encoded opaque scheme-specific-part.
   1305          *
   1306          * @param opaquePart encoded opaque part
   1307          */
   1308         public Builder encodedOpaquePart(String opaquePart) {
   1309             return opaquePart(Part.fromEncoded(opaquePart));
   1310         }
   1311 
   1312         Builder authority(Part authority) {
   1313             // This URI will be hierarchical.
   1314             this.opaquePart = null;
   1315 
   1316             this.authority = authority;
   1317             return this;
   1318         }
   1319 
   1320         /**
   1321          * Encodes and sets the authority.
   1322          */
   1323         public Builder authority(String authority) {
   1324             return authority(Part.fromDecoded(authority));
   1325         }
   1326 
   1327         /**
   1328          * Sets the previously encoded authority.
   1329          */
   1330         public Builder encodedAuthority(String authority) {
   1331             return authority(Part.fromEncoded(authority));
   1332         }
   1333 
   1334         Builder path(PathPart path) {
   1335             // This URI will be hierarchical.
   1336             this.opaquePart = null;
   1337 
   1338             this.path = path;
   1339             return this;
   1340         }
   1341 
   1342         /**
   1343          * Sets the path. Leaves '/' characters intact but encodes others as
   1344          * necessary.
   1345          *
   1346          * <p>If the path is not null and doesn't start with a '/', and if
   1347          * you specify a scheme and/or authority, the builder will prepend the
   1348          * given path with a '/'.
   1349          */
   1350         public Builder path(String path) {
   1351             return path(PathPart.fromDecoded(path));
   1352         }
   1353 
   1354         /**
   1355          * Sets the previously encoded path.
   1356          *
   1357          * <p>If the path is not null and doesn't start with a '/', and if
   1358          * you specify a scheme and/or authority, the builder will prepend the
   1359          * given path with a '/'.
   1360          */
   1361         public Builder encodedPath(String path) {
   1362             return path(PathPart.fromEncoded(path));
   1363         }
   1364 
   1365         /**
   1366          * Encodes the given segment and appends it to the path.
   1367          */
   1368         public Builder appendPath(String newSegment) {
   1369             return path(PathPart.appendDecodedSegment(path, newSegment));
   1370         }
   1371 
   1372         /**
   1373          * Appends the given segment to the path.
   1374          */
   1375         public Builder appendEncodedPath(String newSegment) {
   1376             return path(PathPart.appendEncodedSegment(path, newSegment));
   1377         }
   1378 
   1379         Builder query(Part query) {
   1380             // This URI will be hierarchical.
   1381             this.opaquePart = null;
   1382 
   1383             this.query = query;
   1384             return this;
   1385         }
   1386 
   1387         /**
   1388          * Encodes and sets the query.
   1389          */
   1390         public Builder query(String query) {
   1391             return query(Part.fromDecoded(query));
   1392         }
   1393 
   1394         /**
   1395          * Sets the previously encoded query.
   1396          */
   1397         public Builder encodedQuery(String query) {
   1398             return query(Part.fromEncoded(query));
   1399         }
   1400 
   1401         Builder fragment(Part fragment) {
   1402             this.fragment = fragment;
   1403             return this;
   1404         }
   1405 
   1406         /**
   1407          * Encodes and sets the fragment.
   1408          */
   1409         public Builder fragment(String fragment) {
   1410             return fragment(Part.fromDecoded(fragment));
   1411         }
   1412 
   1413         /**
   1414          * Sets the previously encoded fragment.
   1415          */
   1416         public Builder encodedFragment(String fragment) {
   1417             return fragment(Part.fromEncoded(fragment));
   1418         }
   1419 
   1420         /**
   1421          * Encodes the key and value and then appends the parameter to the
   1422          * query string.
   1423          *
   1424          * @param key which will be encoded
   1425          * @param value which will be encoded
   1426          */
   1427         public Builder appendQueryParameter(String key, String value) {
   1428             // This URI will be hierarchical.
   1429             this.opaquePart = null;
   1430 
   1431             String encodedParameter = encode(key, null) + "="
   1432                     + encode(value, null);
   1433 
   1434             if (query == null) {
   1435                 query = Part.fromEncoded(encodedParameter);
   1436                 return this;
   1437             }
   1438 
   1439             String oldQuery = query.getEncoded();
   1440             if (oldQuery == null || oldQuery.length() == 0) {
   1441                 query = Part.fromEncoded(encodedParameter);
   1442             } else {
   1443                 query = Part.fromEncoded(oldQuery + "&" + encodedParameter);
   1444             }
   1445 
   1446             return this;
   1447         }
   1448 
   1449         /**
   1450          * Constructs a Uri with the current attributes.
   1451          *
   1452          * @throws UnsupportedOperationException if the URI is opaque and the
   1453          *  scheme is null
   1454          */
   1455         public Uri build() {
   1456             if (opaquePart != null) {
   1457                 if (this.scheme == null) {
   1458                     throw new UnsupportedOperationException(
   1459                             "An opaque URI must have a scheme.");
   1460                 }
   1461 
   1462                 return new OpaqueUri(scheme, opaquePart, fragment);
   1463             } else {
   1464                 // Hierarchical URIs should not return null for getPath().
   1465                 PathPart path = this.path;
   1466                 if (path == null || path == PathPart.NULL) {
   1467                     path = PathPart.EMPTY;
   1468                 } else {
   1469                     // If we have a scheme and/or authority, the path must
   1470                     // be absolute. Prepend it with a '/' if necessary.
   1471                     if (hasSchemeOrAuthority()) {
   1472                         path = PathPart.makeAbsolute(path);
   1473                     }
   1474                 }
   1475 
   1476                 return new HierarchicalUri(
   1477                         scheme, authority, path, query, fragment);
   1478             }
   1479         }
   1480 
   1481         private boolean hasSchemeOrAuthority() {
   1482             return scheme != null
   1483                     || (authority != null && authority != Part.NULL);
   1484 
   1485         }
   1486 
   1487         @Override
   1488         public String toString() {
   1489             return build().toString();
   1490         }
   1491     }
   1492 
   1493     /**
   1494      * Searches the query string for parameter values with the given key.
   1495      *
   1496      * @param key which will be encoded
   1497      *
   1498      * @throws UnsupportedOperationException if this isn't a hierarchical URI
   1499      * @throws NullPointerException if key is null
   1500      *
   1501      * @return a list of decoded values
   1502      */
   1503     public List<String> getQueryParameters(String key) {
   1504         if (isOpaque()) {
   1505             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
   1506         }
   1507 
   1508         String query = getEncodedQuery();
   1509         if (query == null) {
   1510             return Collections.emptyList();
   1511         }
   1512 
   1513         String encodedKey;
   1514         try {
   1515             encodedKey = URLEncoder.encode(key, DEFAULT_ENCODING);
   1516         } catch (UnsupportedEncodingException e) {
   1517             throw new AssertionError(e);
   1518         }
   1519 
   1520         // Prepend query with "&" making the first parameter the same as the
   1521         // rest.
   1522         query = "&" + query;
   1523 
   1524         // Parameter prefix.
   1525         String prefix = "&" + encodedKey + "=";
   1526 
   1527         ArrayList<String> values = new ArrayList<String>();
   1528 
   1529         int start = 0;
   1530         int length = query.length();
   1531         while (start < length) {
   1532             start = query.indexOf(prefix, start);
   1533 
   1534             if (start == -1) {
   1535                 // No more values.
   1536                 break;
   1537             }
   1538 
   1539             // Move start to start of value.
   1540             start += prefix.length();
   1541 
   1542             // Find end of value.
   1543             int end = query.indexOf('&', start);
   1544             if (end == -1) {
   1545                 end = query.length();
   1546             }
   1547 
   1548             String value = query.substring(start, end);
   1549             values.add(decode(value));
   1550 
   1551             start = end;
   1552         }
   1553 
   1554         return Collections.unmodifiableList(values);
   1555     }
   1556 
   1557     /**
   1558      * Searches the query string for the first value with the given key.
   1559      *
   1560      * @param key which will be encoded
   1561      * @throws UnsupportedOperationException if this isn't a hierarchical URI
   1562      * @throws NullPointerException if key is null
   1563      *
   1564      * @return the decoded value or null if no parameter is found
   1565      */
   1566     public String getQueryParameter(String key) {
   1567         if (isOpaque()) {
   1568             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
   1569         }
   1570         if (key == null) {
   1571           throw new NullPointerException("key");
   1572         }
   1573 
   1574         final String query = getEncodedQuery();
   1575         if (query == null) {
   1576             return null;
   1577         }
   1578 
   1579         final String encodedKey = encode(key, null);
   1580         final int encodedKeyLength = encodedKey.length();
   1581 
   1582         int encodedKeySearchIndex = 0;
   1583         final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1);
   1584 
   1585         while (encodedKeySearchIndex <= encodedKeySearchEnd) {
   1586             int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex);
   1587             if (keyIndex == -1) {
   1588                 break;
   1589             }
   1590             final int equalsIndex = keyIndex + encodedKeyLength;
   1591             if (equalsIndex >= query.length()) {
   1592                 break;
   1593             }
   1594             if (query.charAt(equalsIndex) != '=') {
   1595                 encodedKeySearchIndex = equalsIndex + 1;
   1596                 continue;
   1597             }
   1598             if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') {
   1599                 int end = query.indexOf('&', equalsIndex);
   1600                 if (end == -1) {
   1601                     end = query.length();
   1602                 }
   1603                 return decode(query.substring(equalsIndex + 1, end));
   1604             } else {
   1605                 encodedKeySearchIndex = equalsIndex + 1;
   1606             }
   1607         }
   1608         return null;
   1609     }
   1610 
   1611     /** Identifies a null parcelled Uri. */
   1612     private static final int NULL_TYPE_ID = 0;
   1613 
   1614     /**
   1615      * Reads Uris from Parcels.
   1616      */
   1617     public static final Parcelable.Creator<Uri> CREATOR
   1618             = new Parcelable.Creator<Uri>() {
   1619         public Uri createFromParcel(Parcel in) {
   1620             int type = in.readInt();
   1621             switch (type) {
   1622                 case NULL_TYPE_ID: return null;
   1623                 case StringUri.TYPE_ID: return StringUri.readFrom(in);
   1624                 case OpaqueUri.TYPE_ID: return OpaqueUri.readFrom(in);
   1625                 case HierarchicalUri.TYPE_ID:
   1626                     return HierarchicalUri.readFrom(in);
   1627             }
   1628 
   1629             throw new AssertionError("Unknown URI type: " + type);
   1630         }
   1631 
   1632         public Uri[] newArray(int size) {
   1633             return new Uri[size];
   1634         }
   1635     };
   1636 
   1637     /**
   1638      * Writes a Uri to a Parcel.
   1639      *
   1640      * @param out parcel to write to
   1641      * @param uri to write, can be null
   1642      */
   1643     public static void writeToParcel(Parcel out, Uri uri) {
   1644         if (uri == null) {
   1645             out.writeInt(NULL_TYPE_ID);
   1646         } else {
   1647             uri.writeToParcel(out, 0);
   1648         }
   1649     }
   1650 
   1651     private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
   1652 
   1653     /**
   1654      * Encodes characters in the given string as '%'-escaped octets
   1655      * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
   1656      * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
   1657      * all other characters.
   1658      *
   1659      * @param s string to encode
   1660      * @return an encoded version of s suitable for use as a URI component,
   1661      *  or null if s is null
   1662      */
   1663     public static String encode(String s) {
   1664         return encode(s, null);
   1665     }
   1666 
   1667     /**
   1668      * Encodes characters in the given string as '%'-escaped octets
   1669      * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
   1670      * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
   1671      * all other characters with the exception of those specified in the
   1672      * allow argument.
   1673      *
   1674      * @param s string to encode
   1675      * @param allow set of additional characters to allow in the encoded form,
   1676      *  null if no characters should be skipped
   1677      * @return an encoded version of s suitable for use as a URI component,
   1678      *  or null if s is null
   1679      */
   1680     public static String encode(String s, String allow) {
   1681         if (s == null) {
   1682             return null;
   1683         }
   1684 
   1685         // Lazily-initialized buffers.
   1686         StringBuilder encoded = null;
   1687 
   1688         int oldLength = s.length();
   1689 
   1690         // This loop alternates between copying over allowed characters and
   1691         // encoding in chunks. This results in fewer method calls and
   1692         // allocations than encoding one character at a time.
   1693         int current = 0;
   1694         while (current < oldLength) {
   1695             // Start in "copying" mode where we copy over allowed chars.
   1696 
   1697             // Find the next character which needs to be encoded.
   1698             int nextToEncode = current;
   1699             while (nextToEncode < oldLength
   1700                     && isAllowed(s.charAt(nextToEncode), allow)) {
   1701                 nextToEncode++;
   1702             }
   1703 
   1704             // If there's nothing more to encode...
   1705             if (nextToEncode == oldLength) {
   1706                 if (current == 0) {
   1707                     // We didn't need to encode anything!
   1708                     return s;
   1709                 } else {
   1710                     // Presumably, we've already done some encoding.
   1711                     encoded.append(s, current, oldLength);
   1712                     return encoded.toString();
   1713                 }
   1714             }
   1715 
   1716             if (encoded == null) {
   1717                 encoded = new StringBuilder();
   1718             }
   1719 
   1720             if (nextToEncode > current) {
   1721                 // Append allowed characters leading up to this point.
   1722                 encoded.append(s, current, nextToEncode);
   1723             } else {
   1724                 // assert nextToEncode == current
   1725             }
   1726 
   1727             // Switch to "encoding" mode.
   1728 
   1729             // Find the next allowed character.
   1730             current = nextToEncode;
   1731             int nextAllowed = current + 1;
   1732             while (nextAllowed < oldLength
   1733                     && !isAllowed(s.charAt(nextAllowed), allow)) {
   1734                 nextAllowed++;
   1735             }
   1736 
   1737             // Convert the substring to bytes and encode the bytes as
   1738             // '%'-escaped octets.
   1739             String toEncode = s.substring(current, nextAllowed);
   1740             try {
   1741                 byte[] bytes = toEncode.getBytes(DEFAULT_ENCODING);
   1742                 int bytesLength = bytes.length;
   1743                 for (int i = 0; i < bytesLength; i++) {
   1744                     encoded.append('%');
   1745                     encoded.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4]);
   1746                     encoded.append(HEX_DIGITS[bytes[i] & 0xf]);
   1747                 }
   1748             } catch (UnsupportedEncodingException e) {
   1749                 throw new AssertionError(e);
   1750             }
   1751 
   1752             current = nextAllowed;
   1753         }
   1754 
   1755         // Encoded could still be null at this point if s is empty.
   1756         return encoded == null ? s : encoded.toString();
   1757     }
   1758 
   1759     /**
   1760      * Returns true if the given character is allowed.
   1761      *
   1762      * @param c character to check
   1763      * @param allow characters to allow
   1764      * @return true if the character is allowed or false if it should be
   1765      *  encoded
   1766      */
   1767     private static boolean isAllowed(char c, String allow) {
   1768         return (c >= 'A' && c <= 'Z')
   1769                 || (c >= 'a' && c <= 'z')
   1770                 || (c >= '0' && c <= '9')
   1771                 || "_-!.~'()*".indexOf(c) != NOT_FOUND
   1772                 || (allow != null && allow.indexOf(c) != NOT_FOUND);
   1773     }
   1774 
   1775     /** Unicode replacement character: \\uFFFD. */
   1776     private static final byte[] REPLACEMENT = { (byte) 0xFF, (byte) 0xFD };
   1777 
   1778     /**
   1779      * Decodes '%'-escaped octets in the given string using the UTF-8 scheme.
   1780      * Replaces invalid octets with the unicode replacement character
   1781      * ("\\uFFFD").
   1782      *
   1783      * @param s encoded string to decode
   1784      * @return the given string with escaped octets decoded, or null if
   1785      *  s is null
   1786      */
   1787     public static String decode(String s) {
   1788         /*
   1789         Compared to java.net.URLEncoderDecoder.decode(), this method decodes a
   1790         chunk at a time instead of one character at a time, and it doesn't
   1791         throw exceptions. It also only allocates memory when necessary--if
   1792         there's nothing to decode, this method won't do much.
   1793         */
   1794 
   1795         if (s == null) {
   1796             return null;
   1797         }
   1798 
   1799         // Lazily-initialized buffers.
   1800         StringBuilder decoded = null;
   1801         ByteArrayOutputStream out = null;
   1802 
   1803         int oldLength = s.length();
   1804 
   1805         // This loop alternates between copying over normal characters and
   1806         // escaping in chunks. This results in fewer method calls and
   1807         // allocations than decoding one character at a time.
   1808         int current = 0;
   1809         while (current < oldLength) {
   1810             // Start in "copying" mode where we copy over normal characters.
   1811 
   1812             // Find the next escape sequence.
   1813             int nextEscape = s.indexOf('%', current);
   1814 
   1815             if (nextEscape == NOT_FOUND) {
   1816                 if (decoded == null) {
   1817                     // We didn't actually decode anything.
   1818                     return s;
   1819                 } else {
   1820                     // Append the remainder and return the decoded string.
   1821                     decoded.append(s, current, oldLength);
   1822                     return decoded.toString();
   1823                 }
   1824             }
   1825 
   1826             // Prepare buffers.
   1827             if (decoded == null) {
   1828                 // Looks like we're going to need the buffers...
   1829                 // We know the new string will be shorter. Using the old length
   1830                 // may overshoot a bit, but it will save us from resizing the
   1831                 // buffer.
   1832                 decoded = new StringBuilder(oldLength);
   1833                 out = new ByteArrayOutputStream(4);
   1834             } else {
   1835                 // Clear decoding buffer.
   1836                 out.reset();
   1837             }
   1838 
   1839             // Append characters leading up to the escape.
   1840             if (nextEscape > current) {
   1841                 decoded.append(s, current, nextEscape);
   1842 
   1843                 current = nextEscape;
   1844             } else {
   1845                 // assert current == nextEscape
   1846             }
   1847 
   1848             // Switch to "decoding" mode where we decode a string of escape
   1849             // sequences.
   1850 
   1851             // Decode and append escape sequences. Escape sequences look like
   1852             // "%ab" where % is literal and a and b are hex digits.
   1853             try {
   1854                 do {
   1855                     if (current + 2 >= oldLength) {
   1856                         // Truncated escape sequence.
   1857                         out.write(REPLACEMENT);
   1858                     } else {
   1859                         int a = Character.digit(s.charAt(current + 1), 16);
   1860                         int b = Character.digit(s.charAt(current + 2), 16);
   1861 
   1862                         if (a == -1 || b == -1) {
   1863                             // Non hex digits.
   1864                             out.write(REPLACEMENT);
   1865                         } else {
   1866                             // Combine the hex digits into one byte and write.
   1867                             out.write((a << 4) + b);
   1868                         }
   1869                     }
   1870 
   1871                     // Move passed the escape sequence.
   1872                     current += 3;
   1873                 } while (current < oldLength && s.charAt(current) == '%');
   1874 
   1875                 // Decode UTF-8 bytes into a string and append it.
   1876                 decoded.append(out.toString(DEFAULT_ENCODING));
   1877             } catch (UnsupportedEncodingException e) {
   1878                 throw new AssertionError(e);
   1879             } catch (IOException e) {
   1880                 throw new AssertionError(e);
   1881             }
   1882         }
   1883 
   1884         // If we don't have a buffer, we didn't have to decode anything.
   1885         return decoded == null ? s : decoded.toString();
   1886     }
   1887 
   1888     /**
   1889      * Support for part implementations.
   1890      */
   1891     static abstract class AbstractPart {
   1892 
   1893         /**
   1894          * Enum which indicates which representation of a given part we have.
   1895          */
   1896         static class Representation {
   1897             static final int BOTH = 0;
   1898             static final int ENCODED = 1;
   1899             static final int DECODED = 2;
   1900         }
   1901 
   1902         volatile String encoded;
   1903         volatile String decoded;
   1904 
   1905         AbstractPart(String encoded, String decoded) {
   1906             this.encoded = encoded;
   1907             this.decoded = decoded;
   1908         }
   1909 
   1910         abstract String getEncoded();
   1911 
   1912         final String getDecoded() {
   1913             @SuppressWarnings("StringEquality")
   1914             boolean hasDecoded = decoded != NOT_CACHED;
   1915             return hasDecoded ? decoded : (decoded = decode(encoded));
   1916         }
   1917 
   1918         final void writeTo(Parcel parcel) {
   1919             @SuppressWarnings("StringEquality")
   1920             boolean hasEncoded = encoded != NOT_CACHED;
   1921 
   1922             @SuppressWarnings("StringEquality")
   1923             boolean hasDecoded = decoded != NOT_CACHED;
   1924 
   1925             if (hasEncoded && hasDecoded) {
   1926                 parcel.writeInt(Representation.BOTH);
   1927                 parcel.writeString(encoded);
   1928                 parcel.writeString(decoded);
   1929             } else if (hasEncoded) {
   1930                 parcel.writeInt(Representation.ENCODED);
   1931                 parcel.writeString(encoded);
   1932             } else if (hasDecoded) {
   1933                 parcel.writeInt(Representation.DECODED);
   1934                 parcel.writeString(decoded);
   1935             } else {
   1936                 throw new AssertionError();
   1937             }
   1938         }
   1939     }
   1940 
   1941     /**
   1942      * Immutable wrapper of encoded and decoded versions of a URI part. Lazily
   1943      * creates the encoded or decoded version from the other.
   1944      */
   1945     static class Part extends AbstractPart {
   1946 
   1947         /** A part with null values. */
   1948         static final Part NULL = new EmptyPart(null);
   1949 
   1950         /** A part with empty strings for values. */
   1951         static final Part EMPTY = new EmptyPart("");
   1952 
   1953         private Part(String encoded, String decoded) {
   1954             super(encoded, decoded);
   1955         }
   1956 
   1957         boolean isEmpty() {
   1958             return false;
   1959         }
   1960 
   1961         String getEncoded() {
   1962             @SuppressWarnings("StringEquality")
   1963             boolean hasEncoded = encoded != NOT_CACHED;
   1964             return hasEncoded ? encoded : (encoded = encode(decoded));
   1965         }
   1966 
   1967         static Part readFrom(Parcel parcel) {
   1968             int representation = parcel.readInt();
   1969             switch (representation) {
   1970                 case Representation.BOTH:
   1971                     return from(parcel.readString(), parcel.readString());
   1972                 case Representation.ENCODED:
   1973                     return fromEncoded(parcel.readString());
   1974                 case Representation.DECODED:
   1975                     return fromDecoded(parcel.readString());
   1976                 default:
   1977                     throw new AssertionError();
   1978             }
   1979         }
   1980 
   1981         /**
   1982          * Returns given part or {@link #NULL} if the given part is null.
   1983          */
   1984         static Part nonNull(Part part) {
   1985             return part == null ? NULL : part;
   1986         }
   1987 
   1988         /**
   1989          * Creates a part from the encoded string.
   1990          *
   1991          * @param encoded part string
   1992          */
   1993         static Part fromEncoded(String encoded) {
   1994             return from(encoded, NOT_CACHED);
   1995         }
   1996 
   1997         /**
   1998          * Creates a part from the decoded string.
   1999          *
   2000          * @param decoded part string
   2001          */
   2002         static Part fromDecoded(String decoded) {
   2003             return from(NOT_CACHED, decoded);
   2004         }
   2005 
   2006         /**
   2007          * Creates a part from the encoded and decoded strings.
   2008          *
   2009          * @param encoded part string
   2010          * @param decoded part string
   2011          */
   2012         static Part from(String encoded, String decoded) {
   2013             // We have to check both encoded and decoded in case one is
   2014             // NOT_CACHED.
   2015 
   2016             if (encoded == null) {
   2017                 return NULL;
   2018             }
   2019             if (encoded.length() == 0) {
   2020                 return EMPTY;
   2021             }
   2022 
   2023             if (decoded == null) {
   2024                 return NULL;
   2025             }
   2026             if (decoded .length() == 0) {
   2027                 return EMPTY;
   2028             }
   2029 
   2030             return new Part(encoded, decoded);
   2031         }
   2032 
   2033         private static class EmptyPart extends Part {
   2034             public EmptyPart(String value) {
   2035                 super(value, value);
   2036             }
   2037 
   2038             @Override
   2039             boolean isEmpty() {
   2040                 return true;
   2041             }
   2042         }
   2043     }
   2044 
   2045     /**
   2046      * Immutable wrapper of encoded and decoded versions of a path part. Lazily
   2047      * creates the encoded or decoded version from the other.
   2048      */
   2049     static class PathPart extends AbstractPart {
   2050 
   2051         /** A part with null values. */
   2052         static final PathPart NULL = new PathPart(null, null);
   2053 
   2054         /** A part with empty strings for values. */
   2055         static final PathPart EMPTY = new PathPart("", "");
   2056 
   2057         private PathPart(String encoded, String decoded) {
   2058             super(encoded, decoded);
   2059         }
   2060 
   2061         String getEncoded() {
   2062             @SuppressWarnings("StringEquality")
   2063             boolean hasEncoded = encoded != NOT_CACHED;
   2064 
   2065             // Don't encode '/'.
   2066             return hasEncoded ? encoded : (encoded = encode(decoded, "/"));
   2067         }
   2068 
   2069         /**
   2070          * Cached path segments. This doesn't need to be volatile--we don't
   2071          * care if other threads see the result.
   2072          */
   2073         private PathSegments pathSegments;
   2074 
   2075         /**
   2076          * Gets the individual path segments. Parses them if necessary.
   2077          *
   2078          * @return parsed path segments or null if this isn't a hierarchical
   2079          *  URI
   2080          */
   2081         PathSegments getPathSegments() {
   2082             if (pathSegments != null) {
   2083                 return pathSegments;
   2084             }
   2085 
   2086             String path = getEncoded();
   2087             if (path == null) {
   2088                 return pathSegments = PathSegments.EMPTY;
   2089             }
   2090 
   2091             PathSegmentsBuilder segmentBuilder = new PathSegmentsBuilder();
   2092 
   2093             int previous = 0;
   2094             int current;
   2095             while ((current = path.indexOf('/', previous)) > -1) {
   2096                 // This check keeps us from adding a segment if the path starts
   2097                 // '/' and an empty segment for "//".
   2098                 if (previous < current) {
   2099                     String decodedSegment
   2100                             = decode(path.substring(previous, current));
   2101                     segmentBuilder.add(decodedSegment);
   2102                 }
   2103                 previous = current + 1;
   2104             }
   2105 
   2106             // Add in the final path segment.
   2107             if (previous < path.length()) {
   2108                 segmentBuilder.add(decode(path.substring(previous)));
   2109             }
   2110 
   2111             return pathSegments = segmentBuilder.build();
   2112         }
   2113 
   2114         static PathPart appendEncodedSegment(PathPart oldPart,
   2115                 String newSegment) {
   2116             // If there is no old path, should we make the new path relative
   2117             // or absolute? I pick absolute.
   2118 
   2119             if (oldPart == null) {
   2120                 // No old path.
   2121                 return fromEncoded("/" + newSegment);
   2122             }
   2123 
   2124             String oldPath = oldPart.getEncoded();
   2125 
   2126             if (oldPath == null) {
   2127                 oldPath = "";
   2128             }
   2129 
   2130             int oldPathLength = oldPath.length();
   2131             String newPath;
   2132             if (oldPathLength == 0) {
   2133                 // No old path.
   2134                 newPath = "/" + newSegment;
   2135             } else if (oldPath.charAt(oldPathLength - 1) == '/') {
   2136                 newPath = oldPath + newSegment;
   2137             } else {
   2138                 newPath = oldPath + "/" + newSegment;
   2139             }
   2140 
   2141             return fromEncoded(newPath);
   2142         }
   2143 
   2144         static PathPart appendDecodedSegment(PathPart oldPart, String decoded) {
   2145             String encoded = encode(decoded);
   2146 
   2147             // TODO: Should we reuse old PathSegments? Probably not.
   2148             return appendEncodedSegment(oldPart, encoded);
   2149         }
   2150 
   2151         static PathPart readFrom(Parcel parcel) {
   2152             int representation = parcel.readInt();
   2153             switch (representation) {
   2154                 case Representation.BOTH:
   2155                     return from(parcel.readString(), parcel.readString());
   2156                 case Representation.ENCODED:
   2157                     return fromEncoded(parcel.readString());
   2158                 case Representation.DECODED:
   2159                     return fromDecoded(parcel.readString());
   2160                 default:
   2161                     throw new AssertionError();
   2162             }
   2163         }
   2164 
   2165         /**
   2166          * Creates a path from the encoded string.
   2167          *
   2168          * @param encoded part string
   2169          */
   2170         static PathPart fromEncoded(String encoded) {
   2171             return from(encoded, NOT_CACHED);
   2172         }
   2173 
   2174         /**
   2175          * Creates a path from the decoded string.
   2176          *
   2177          * @param decoded part string
   2178          */
   2179         static PathPart fromDecoded(String decoded) {
   2180             return from(NOT_CACHED, decoded);
   2181         }
   2182 
   2183         /**
   2184          * Creates a path from the encoded and decoded strings.
   2185          *
   2186          * @param encoded part string
   2187          * @param decoded part string
   2188          */
   2189         static PathPart from(String encoded, String decoded) {
   2190             if (encoded == null) {
   2191                 return NULL;
   2192             }
   2193 
   2194             if (encoded.length() == 0) {
   2195                 return EMPTY;
   2196             }
   2197 
   2198             return new PathPart(encoded, decoded);
   2199         }
   2200 
   2201         /**
   2202          * Prepends path values with "/" if they're present, not empty, and
   2203          * they don't already start with "/".
   2204          */
   2205         static PathPart makeAbsolute(PathPart oldPart) {
   2206             @SuppressWarnings("StringEquality")
   2207             boolean encodedCached = oldPart.encoded != NOT_CACHED;
   2208 
   2209             // We don't care which version we use, and we don't want to force
   2210             // unneccessary encoding/decoding.
   2211             String oldPath = encodedCached ? oldPart.encoded : oldPart.decoded;
   2212 
   2213             if (oldPath == null || oldPath.length() == 0
   2214                     || oldPath.startsWith("/")) {
   2215                 return oldPart;
   2216             }
   2217 
   2218             // Prepend encoded string if present.
   2219             String newEncoded = encodedCached
   2220                     ? "/" + oldPart.encoded : NOT_CACHED;
   2221 
   2222             // Prepend decoded string if present.
   2223             @SuppressWarnings("StringEquality")
   2224             boolean decodedCached = oldPart.decoded != NOT_CACHED;
   2225             String newDecoded = decodedCached
   2226                     ? "/" + oldPart.decoded
   2227                     : NOT_CACHED;
   2228 
   2229             return new PathPart(newEncoded, newDecoded);
   2230         }
   2231     }
   2232 
   2233     /**
   2234      * Creates a new Uri by appending an already-encoded path segment to a
   2235      * base Uri.
   2236      *
   2237      * @param baseUri Uri to append path segment to
   2238      * @param pathSegment encoded path segment to append
   2239      * @return a new Uri based on baseUri with the given segment appended to
   2240      *  the path
   2241      * @throws NullPointerException if baseUri is null
   2242      */
   2243     public static Uri withAppendedPath(Uri baseUri, String pathSegment) {
   2244         Builder builder = baseUri.buildUpon();
   2245         builder = builder.appendEncodedPath(pathSegment);
   2246         return builder.build();
   2247     }
   2248 }
   2249