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