1/*
2 * Copyright (C) 2006 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 com.android.internal.util;
18
19import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.graphics.Bitmap.CompressFormat;
22import android.net.Uri;
23import android.text.TextUtils;
24import android.util.ArrayMap;
25import android.util.Base64;
26import android.util.Xml;
27
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30import org.xmlpull.v1.XmlSerializer;
31
32import java.io.ByteArrayOutputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.net.ProtocolException;
37import java.nio.charset.StandardCharsets;
38import java.util.ArrayList;
39import java.util.HashMap;
40import java.util.HashSet;
41import java.util.Iterator;
42import java.util.List;
43import java.util.Map;
44import java.util.Set;
45
46/** {@hide} */
47public class XmlUtils {
48
49    private static final String STRING_ARRAY_SEPARATOR = ":";
50
51    public static void skipCurrentTag(XmlPullParser parser)
52            throws XmlPullParserException, IOException {
53        int outerDepth = parser.getDepth();
54        int type;
55        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
56               && (type != XmlPullParser.END_TAG
57                       || parser.getDepth() > outerDepth)) {
58        }
59    }
60
61    public static final int
62    convertValueToList(CharSequence value, String[] options, int defaultValue)
63    {
64        if (null != value) {
65            for (int i = 0; i < options.length; i++) {
66                if (value.equals(options[i]))
67                    return i;
68            }
69        }
70
71        return defaultValue;
72    }
73
74    public static final boolean
75    convertValueToBoolean(CharSequence value, boolean defaultValue)
76    {
77        boolean result = false;
78
79        if (null == value)
80            return defaultValue;
81
82        if (value.equals("1")
83        ||  value.equals("true")
84        ||  value.equals("TRUE"))
85            result = true;
86
87        return result;
88    }
89
90    public static final int
91    convertValueToInt(CharSequence charSeq, int defaultValue)
92    {
93        if (null == charSeq)
94            return defaultValue;
95
96        String nm = charSeq.toString();
97
98        // XXX This code is copied from Integer.decode() so we don't
99        // have to instantiate an Integer!
100
101        int value;
102        int sign = 1;
103        int index = 0;
104        int len = nm.length();
105        int base = 10;
106
107        if ('-' == nm.charAt(0)) {
108            sign = -1;
109            index++;
110        }
111
112        if ('0' == nm.charAt(index)) {
113            //  Quick check for a zero by itself
114            if (index == (len - 1))
115                return 0;
116
117            char    c = nm.charAt(index + 1);
118
119            if ('x' == c || 'X' == c) {
120                index += 2;
121                base = 16;
122            } else {
123                index++;
124                base = 8;
125            }
126        }
127        else if ('#' == nm.charAt(index))
128        {
129            index++;
130            base = 16;
131        }
132
133        return Integer.parseInt(nm.substring(index), base) * sign;
134    }
135
136    public static int convertValueToUnsignedInt(String value, int defaultValue) {
137        if (null == value) {
138            return defaultValue;
139        }
140
141        return parseUnsignedIntAttribute(value);
142    }
143
144    public static int parseUnsignedIntAttribute(CharSequence charSeq) {
145        String  value = charSeq.toString();
146
147        long    bits;
148        int     index = 0;
149        int     len = value.length();
150        int     base = 10;
151
152        if ('0' == value.charAt(index)) {
153            //  Quick check for zero by itself
154            if (index == (len - 1))
155                return 0;
156
157            char    c = value.charAt(index + 1);
158
159            if ('x' == c || 'X' == c) {     //  check for hex
160                index += 2;
161                base = 16;
162            } else {                        //  check for octal
163                index++;
164                base = 8;
165            }
166        } else if ('#' == value.charAt(index)) {
167            index++;
168            base = 16;
169        }
170
171        return (int) Long.parseLong(value.substring(index), base);
172    }
173
174    /**
175     * Flatten a Map into an output stream as XML.  The map can later be
176     * read back with readMapXml().
177     *
178     * @param val The map to be flattened.
179     * @param out Where to write the XML data.
180     *
181     * @see #writeMapXml(Map, String, XmlSerializer)
182     * @see #writeListXml
183     * @see #writeValueXml
184     * @see #readMapXml
185     */
186    public static final void writeMapXml(Map val, OutputStream out)
187            throws XmlPullParserException, java.io.IOException {
188        XmlSerializer serializer = new FastXmlSerializer();
189        serializer.setOutput(out, StandardCharsets.UTF_8.name());
190        serializer.startDocument(null, true);
191        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
192        writeMapXml(val, null, serializer);
193        serializer.endDocument();
194    }
195
196    /**
197     * Flatten a List into an output stream as XML.  The list can later be
198     * read back with readListXml().
199     *
200     * @param val The list to be flattened.
201     * @param out Where to write the XML data.
202     *
203     * @see #writeListXml(List, String, XmlSerializer)
204     * @see #writeMapXml
205     * @see #writeValueXml
206     * @see #readListXml
207     */
208    public static final void writeListXml(List val, OutputStream out)
209    throws XmlPullParserException, java.io.IOException
210    {
211        XmlSerializer serializer = Xml.newSerializer();
212        serializer.setOutput(out, StandardCharsets.UTF_8.name());
213        serializer.startDocument(null, true);
214        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
215        writeListXml(val, null, serializer);
216        serializer.endDocument();
217    }
218
219    /**
220     * Flatten a Map into an XmlSerializer.  The map can later be read back
221     * with readThisMapXml().
222     *
223     * @param val The map to be flattened.
224     * @param name Name attribute to include with this list's tag, or null for
225     *             none.
226     * @param out XmlSerializer to write the map into.
227     *
228     * @see #writeMapXml(Map, OutputStream)
229     * @see #writeListXml
230     * @see #writeValueXml
231     * @see #readMapXml
232     */
233    public static final void writeMapXml(Map val, String name, XmlSerializer out)
234            throws XmlPullParserException, java.io.IOException {
235        writeMapXml(val, name, out, null);
236    }
237
238    /**
239     * Flatten a Map into an XmlSerializer.  The map can later be read back
240     * with readThisMapXml().
241     *
242     * @param val The map to be flattened.
243     * @param name Name attribute to include with this list's tag, or null for
244     *             none.
245     * @param out XmlSerializer to write the map into.
246     * @param callback Method to call when an Object type is not recognized.
247     *
248     * @see #writeMapXml(Map, OutputStream)
249     * @see #writeListXml
250     * @see #writeValueXml
251     * @see #readMapXml
252     *
253     * @hide
254     */
255    public static final void writeMapXml(Map val, String name, XmlSerializer out,
256            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
257
258        if (val == null) {
259            out.startTag(null, "null");
260            out.endTag(null, "null");
261            return;
262        }
263
264        out.startTag(null, "map");
265        if (name != null) {
266            out.attribute(null, "name", name);
267        }
268
269        writeMapXml(val, out, callback);
270
271        out.endTag(null, "map");
272    }
273
274    /**
275     * Flatten a Map into an XmlSerializer.  The map can later be read back
276     * with readThisMapXml(). This method presumes that the start tag and
277     * name attribute have already been written and does not write an end tag.
278     *
279     * @param val The map to be flattened.
280     * @param out XmlSerializer to write the map into.
281     *
282     * @see #writeMapXml(Map, OutputStream)
283     * @see #writeListXml
284     * @see #writeValueXml
285     * @see #readMapXml
286     *
287     * @hide
288     */
289    public static final void writeMapXml(Map val, XmlSerializer out,
290            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
291        if (val == null) {
292            return;
293        }
294
295        Set s = val.entrySet();
296        Iterator i = s.iterator();
297
298        while (i.hasNext()) {
299            Map.Entry e = (Map.Entry)i.next();
300            writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
301        }
302    }
303
304    /**
305     * Flatten a List into an XmlSerializer.  The list can later be read back
306     * with readThisListXml().
307     *
308     * @param val The list to be flattened.
309     * @param name Name attribute to include with this list's tag, or null for
310     *             none.
311     * @param out XmlSerializer to write the list into.
312     *
313     * @see #writeListXml(List, OutputStream)
314     * @see #writeMapXml
315     * @see #writeValueXml
316     * @see #readListXml
317     */
318    public static final void writeListXml(List val, String name, XmlSerializer out)
319    throws XmlPullParserException, java.io.IOException
320    {
321        if (val == null) {
322            out.startTag(null, "null");
323            out.endTag(null, "null");
324            return;
325        }
326
327        out.startTag(null, "list");
328        if (name != null) {
329            out.attribute(null, "name", name);
330        }
331
332        int N = val.size();
333        int i=0;
334        while (i < N) {
335            writeValueXml(val.get(i), null, out);
336            i++;
337        }
338
339        out.endTag(null, "list");
340    }
341
342    public static final void writeSetXml(Set val, String name, XmlSerializer out)
343            throws XmlPullParserException, java.io.IOException {
344        if (val == null) {
345            out.startTag(null, "null");
346            out.endTag(null, "null");
347            return;
348        }
349
350        out.startTag(null, "set");
351        if (name != null) {
352            out.attribute(null, "name", name);
353        }
354
355        for (Object v : val) {
356            writeValueXml(v, null, out);
357        }
358
359        out.endTag(null, "set");
360    }
361
362    /**
363     * Flatten a byte[] into an XmlSerializer.  The list can later be read back
364     * with readThisByteArrayXml().
365     *
366     * @param val The byte array to be flattened.
367     * @param name Name attribute to include with this array's tag, or null for
368     *             none.
369     * @param out XmlSerializer to write the array into.
370     *
371     * @see #writeMapXml
372     * @see #writeValueXml
373     */
374    public static final void writeByteArrayXml(byte[] val, String name,
375            XmlSerializer out)
376            throws XmlPullParserException, java.io.IOException {
377
378        if (val == null) {
379            out.startTag(null, "null");
380            out.endTag(null, "null");
381            return;
382        }
383
384        out.startTag(null, "byte-array");
385        if (name != null) {
386            out.attribute(null, "name", name);
387        }
388
389        final int N = val.length;
390        out.attribute(null, "num", Integer.toString(N));
391
392        StringBuilder sb = new StringBuilder(val.length*2);
393        for (int i=0; i<N; i++) {
394            int b = val[i];
395            int h = b>>4;
396            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
397            h = b&0xff;
398            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
399        }
400
401        out.text(sb.toString());
402
403        out.endTag(null, "byte-array");
404    }
405
406    /**
407     * Flatten an int[] into an XmlSerializer.  The list can later be read back
408     * with readThisIntArrayXml().
409     *
410     * @param val The int array to be flattened.
411     * @param name Name attribute to include with this array's tag, or null for
412     *             none.
413     * @param out XmlSerializer to write the array into.
414     *
415     * @see #writeMapXml
416     * @see #writeValueXml
417     * @see #readThisIntArrayXml
418     */
419    public static final void writeIntArrayXml(int[] val, String name,
420            XmlSerializer out)
421            throws XmlPullParserException, java.io.IOException {
422
423        if (val == null) {
424            out.startTag(null, "null");
425            out.endTag(null, "null");
426            return;
427        }
428
429        out.startTag(null, "int-array");
430        if (name != null) {
431            out.attribute(null, "name", name);
432        }
433
434        final int N = val.length;
435        out.attribute(null, "num", Integer.toString(N));
436
437        for (int i=0; i<N; i++) {
438            out.startTag(null, "item");
439            out.attribute(null, "value", Integer.toString(val[i]));
440            out.endTag(null, "item");
441        }
442
443        out.endTag(null, "int-array");
444    }
445
446    /**
447     * Flatten a long[] into an XmlSerializer.  The list can later be read back
448     * with readThisLongArrayXml().
449     *
450     * @param val The long array to be flattened.
451     * @param name Name attribute to include with this array's tag, or null for
452     *             none.
453     * @param out XmlSerializer to write the array into.
454     *
455     * @see #writeMapXml
456     * @see #writeValueXml
457     * @see #readThisIntArrayXml
458     */
459    public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
460            throws XmlPullParserException, java.io.IOException {
461
462        if (val == null) {
463            out.startTag(null, "null");
464            out.endTag(null, "null");
465            return;
466        }
467
468        out.startTag(null, "long-array");
469        if (name != null) {
470            out.attribute(null, "name", name);
471        }
472
473        final int N = val.length;
474        out.attribute(null, "num", Integer.toString(N));
475
476        for (int i=0; i<N; i++) {
477            out.startTag(null, "item");
478            out.attribute(null, "value", Long.toString(val[i]));
479            out.endTag(null, "item");
480        }
481
482        out.endTag(null, "long-array");
483    }
484
485    /**
486     * Flatten a double[] into an XmlSerializer.  The list can later be read back
487     * with readThisDoubleArrayXml().
488     *
489     * @param val The double array to be flattened.
490     * @param name Name attribute to include with this array's tag, or null for
491     *             none.
492     * @param out XmlSerializer to write the array into.
493     *
494     * @see #writeMapXml
495     * @see #writeValueXml
496     * @see #readThisIntArrayXml
497     */
498    public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
499            throws XmlPullParserException, java.io.IOException {
500
501        if (val == null) {
502            out.startTag(null, "null");
503            out.endTag(null, "null");
504            return;
505        }
506
507        out.startTag(null, "double-array");
508        if (name != null) {
509            out.attribute(null, "name", name);
510        }
511
512        final int N = val.length;
513        out.attribute(null, "num", Integer.toString(N));
514
515        for (int i=0; i<N; i++) {
516            out.startTag(null, "item");
517            out.attribute(null, "value", Double.toString(val[i]));
518            out.endTag(null, "item");
519        }
520
521        out.endTag(null, "double-array");
522    }
523
524    /**
525     * Flatten a String[] into an XmlSerializer.  The list can later be read back
526     * with readThisStringArrayXml().
527     *
528     * @param val The String array to be flattened.
529     * @param name Name attribute to include with this array's tag, or null for
530     *             none.
531     * @param out XmlSerializer to write the array into.
532     *
533     * @see #writeMapXml
534     * @see #writeValueXml
535     * @see #readThisIntArrayXml
536     */
537    public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
538            throws XmlPullParserException, java.io.IOException {
539
540        if (val == null) {
541            out.startTag(null, "null");
542            out.endTag(null, "null");
543            return;
544        }
545
546        out.startTag(null, "string-array");
547        if (name != null) {
548            out.attribute(null, "name", name);
549        }
550
551        final int N = val.length;
552        out.attribute(null, "num", Integer.toString(N));
553
554        for (int i=0; i<N; i++) {
555            out.startTag(null, "item");
556            out.attribute(null, "value", val[i]);
557            out.endTag(null, "item");
558        }
559
560        out.endTag(null, "string-array");
561    }
562
563    /**
564     * Flatten a boolean[] into an XmlSerializer.  The list can later be read back
565     * with readThisBooleanArrayXml().
566     *
567     * @param val The boolean array to be flattened.
568     * @param name Name attribute to include with this array's tag, or null for
569     *             none.
570     * @param out XmlSerializer to write the array into.
571     *
572     * @see #writeMapXml
573     * @see #writeValueXml
574     * @see #readThisIntArrayXml
575     */
576    public static final void writeBooleanArrayXml(boolean[] val, String name, XmlSerializer out)
577            throws XmlPullParserException, java.io.IOException {
578
579        if (val == null) {
580            out.startTag(null, "null");
581            out.endTag(null, "null");
582            return;
583        }
584
585        out.startTag(null, "boolean-array");
586        if (name != null) {
587            out.attribute(null, "name", name);
588        }
589
590        final int N = val.length;
591        out.attribute(null, "num", Integer.toString(N));
592
593        for (int i=0; i<N; i++) {
594            out.startTag(null, "item");
595            out.attribute(null, "value", Boolean.toString(val[i]));
596            out.endTag(null, "item");
597        }
598
599        out.endTag(null, "boolean-array");
600    }
601
602    /**
603     * Flatten an object's value into an XmlSerializer.  The value can later
604     * be read back with readThisValueXml().
605     *
606     * Currently supported value types are: null, String, Integer, Long,
607     * Float, Double Boolean, Map, List.
608     *
609     * @param v The object to be flattened.
610     * @param name Name attribute to include with this value's tag, or null
611     *             for none.
612     * @param out XmlSerializer to write the object into.
613     *
614     * @see #writeMapXml
615     * @see #writeListXml
616     * @see #readValueXml
617     */
618    public static final void writeValueXml(Object v, String name, XmlSerializer out)
619            throws XmlPullParserException, java.io.IOException {
620        writeValueXml(v, name, out, null);
621    }
622
623    /**
624     * Flatten an object's value into an XmlSerializer.  The value can later
625     * be read back with readThisValueXml().
626     *
627     * Currently supported value types are: null, String, Integer, Long,
628     * Float, Double Boolean, Map, List.
629     *
630     * @param v The object to be flattened.
631     * @param name Name attribute to include with this value's tag, or null
632     *             for none.
633     * @param out XmlSerializer to write the object into.
634     * @param callback Handler for Object types not recognized.
635     *
636     * @see #writeMapXml
637     * @see #writeListXml
638     * @see #readValueXml
639     */
640    private static final void writeValueXml(Object v, String name, XmlSerializer out,
641            WriteMapCallback callback)  throws XmlPullParserException, java.io.IOException {
642        String typeStr;
643        if (v == null) {
644            out.startTag(null, "null");
645            if (name != null) {
646                out.attribute(null, "name", name);
647            }
648            out.endTag(null, "null");
649            return;
650        } else if (v instanceof String) {
651            out.startTag(null, "string");
652            if (name != null) {
653                out.attribute(null, "name", name);
654            }
655            out.text(v.toString());
656            out.endTag(null, "string");
657            return;
658        } else if (v instanceof Integer) {
659            typeStr = "int";
660        } else if (v instanceof Long) {
661            typeStr = "long";
662        } else if (v instanceof Float) {
663            typeStr = "float";
664        } else if (v instanceof Double) {
665            typeStr = "double";
666        } else if (v instanceof Boolean) {
667            typeStr = "boolean";
668        } else if (v instanceof byte[]) {
669            writeByteArrayXml((byte[])v, name, out);
670            return;
671        } else if (v instanceof int[]) {
672            writeIntArrayXml((int[])v, name, out);
673            return;
674        } else if (v instanceof long[]) {
675            writeLongArrayXml((long[])v, name, out);
676            return;
677        } else if (v instanceof double[]) {
678            writeDoubleArrayXml((double[])v, name, out);
679            return;
680        } else if (v instanceof String[]) {
681            writeStringArrayXml((String[])v, name, out);
682            return;
683        } else if (v instanceof boolean[]) {
684            writeBooleanArrayXml((boolean[])v, name, out);
685            return;
686        } else if (v instanceof Map) {
687            writeMapXml((Map)v, name, out);
688            return;
689        } else if (v instanceof List) {
690            writeListXml((List) v, name, out);
691            return;
692        } else if (v instanceof Set) {
693            writeSetXml((Set) v, name, out);
694            return;
695        } else if (v instanceof CharSequence) {
696            // XXX This is to allow us to at least write something if
697            // we encounter styled text...  but it means we will drop all
698            // of the styling information. :(
699            out.startTag(null, "string");
700            if (name != null) {
701                out.attribute(null, "name", name);
702            }
703            out.text(v.toString());
704            out.endTag(null, "string");
705            return;
706        } else if (callback != null) {
707            callback.writeUnknownObject(v, name, out);
708            return;
709        } else {
710            throw new RuntimeException("writeValueXml: unable to write value " + v);
711        }
712
713        out.startTag(null, typeStr);
714        if (name != null) {
715            out.attribute(null, "name", name);
716        }
717        out.attribute(null, "value", v.toString());
718        out.endTag(null, typeStr);
719    }
720
721    /**
722     * Read a HashMap from an InputStream containing XML.  The stream can
723     * previously have been written by writeMapXml().
724     *
725     * @param in The InputStream from which to read.
726     *
727     * @return HashMap The resulting map.
728     *
729     * @see #readListXml
730     * @see #readValueXml
731     * @see #readThisMapXml
732     * #see #writeMapXml
733     */
734    @SuppressWarnings("unchecked")
735    public static final HashMap<String, ?> readMapXml(InputStream in)
736    throws XmlPullParserException, java.io.IOException
737    {
738        XmlPullParser   parser = Xml.newPullParser();
739        parser.setInput(in, StandardCharsets.UTF_8.name());
740        return (HashMap<String, ?>) readValueXml(parser, new String[1]);
741    }
742
743    /**
744     * Read an ArrayList from an InputStream containing XML.  The stream can
745     * previously have been written by writeListXml().
746     *
747     * @param in The InputStream from which to read.
748     *
749     * @return ArrayList The resulting list.
750     *
751     * @see #readMapXml
752     * @see #readValueXml
753     * @see #readThisListXml
754     * @see #writeListXml
755     */
756    public static final ArrayList readListXml(InputStream in)
757    throws XmlPullParserException, java.io.IOException
758    {
759        XmlPullParser   parser = Xml.newPullParser();
760        parser.setInput(in, StandardCharsets.UTF_8.name());
761        return (ArrayList)readValueXml(parser, new String[1]);
762    }
763
764
765    /**
766     * Read a HashSet from an InputStream containing XML. The stream can
767     * previously have been written by writeSetXml().
768     *
769     * @param in The InputStream from which to read.
770     *
771     * @return HashSet The resulting set.
772     *
773     * @throws XmlPullParserException
774     * @throws java.io.IOException
775     *
776     * @see #readValueXml
777     * @see #readThisSetXml
778     * @see #writeSetXml
779     */
780    public static final HashSet readSetXml(InputStream in)
781            throws XmlPullParserException, java.io.IOException {
782        XmlPullParser parser = Xml.newPullParser();
783        parser.setInput(in, null);
784        return (HashSet) readValueXml(parser, new String[1]);
785    }
786
787    /**
788     * Read a HashMap object from an XmlPullParser.  The XML data could
789     * previously have been generated by writeMapXml().  The XmlPullParser
790     * must be positioned <em>after</em> the tag that begins the map.
791     *
792     * @param parser The XmlPullParser from which to read the map data.
793     * @param endTag Name of the tag that will end the map, usually "map".
794     * @param name An array of one string, used to return the name attribute
795     *             of the map's tag.
796     *
797     * @return HashMap The newly generated map.
798     *
799     * @see #readMapXml
800     */
801    public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
802            String[] name) throws XmlPullParserException, java.io.IOException {
803        return readThisMapXml(parser, endTag, name, null);
804    }
805
806    /**
807     * Read a HashMap object from an XmlPullParser.  The XML data could
808     * previously have been generated by writeMapXml().  The XmlPullParser
809     * must be positioned <em>after</em> the tag that begins the map.
810     *
811     * @param parser The XmlPullParser from which to read the map data.
812     * @param endTag Name of the tag that will end the map, usually "map".
813     * @param name An array of one string, used to return the name attribute
814     *             of the map's tag.
815     *
816     * @return HashMap The newly generated map.
817     *
818     * @see #readMapXml
819     * @hide
820     */
821    public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
822            String[] name, ReadMapCallback callback)
823            throws XmlPullParserException, java.io.IOException
824    {
825        HashMap<String, Object> map = new HashMap<String, Object>();
826
827        int eventType = parser.getEventType();
828        do {
829            if (eventType == parser.START_TAG) {
830                Object val = readThisValueXml(parser, name, callback, false);
831                map.put(name[0], val);
832            } else if (eventType == parser.END_TAG) {
833                if (parser.getName().equals(endTag)) {
834                    return map;
835                }
836                throw new XmlPullParserException(
837                    "Expected " + endTag + " end tag at: " + parser.getName());
838            }
839            eventType = parser.next();
840        } while (eventType != parser.END_DOCUMENT);
841
842        throw new XmlPullParserException(
843            "Document ended before " + endTag + " end tag");
844    }
845
846    /**
847     * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap.
848     * @hide
849     */
850    public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag,
851            String[] name, ReadMapCallback callback)
852            throws XmlPullParserException, java.io.IOException
853    {
854        ArrayMap<String, Object> map = new ArrayMap<>();
855
856        int eventType = parser.getEventType();
857        do {
858            if (eventType == parser.START_TAG) {
859                Object val = readThisValueXml(parser, name, callback, true);
860                map.put(name[0], val);
861            } else if (eventType == parser.END_TAG) {
862                if (parser.getName().equals(endTag)) {
863                    return map;
864                }
865                throw new XmlPullParserException(
866                    "Expected " + endTag + " end tag at: " + parser.getName());
867            }
868            eventType = parser.next();
869        } while (eventType != parser.END_DOCUMENT);
870
871        throw new XmlPullParserException(
872            "Document ended before " + endTag + " end tag");
873    }
874
875    /**
876     * Read an ArrayList object from an XmlPullParser.  The XML data could
877     * previously have been generated by writeListXml().  The XmlPullParser
878     * must be positioned <em>after</em> the tag that begins the list.
879     *
880     * @param parser The XmlPullParser from which to read the list data.
881     * @param endTag Name of the tag that will end the list, usually "list".
882     * @param name An array of one string, used to return the name attribute
883     *             of the list's tag.
884     *
885     * @return HashMap The newly generated list.
886     *
887     * @see #readListXml
888     */
889    public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
890            String[] name) throws XmlPullParserException, java.io.IOException {
891        return readThisListXml(parser, endTag, name, null, false);
892    }
893
894    /**
895     * Read an ArrayList object from an XmlPullParser.  The XML data could
896     * previously have been generated by writeListXml().  The XmlPullParser
897     * must be positioned <em>after</em> the tag that begins the list.
898     *
899     * @param parser The XmlPullParser from which to read the list data.
900     * @param endTag Name of the tag that will end the list, usually "list".
901     * @param name An array of one string, used to return the name attribute
902     *             of the list's tag.
903     *
904     * @return HashMap The newly generated list.
905     *
906     * @see #readListXml
907     */
908    private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
909            String[] name, ReadMapCallback callback, boolean arrayMap)
910            throws XmlPullParserException, java.io.IOException {
911        ArrayList list = new ArrayList();
912
913        int eventType = parser.getEventType();
914        do {
915            if (eventType == parser.START_TAG) {
916                Object val = readThisValueXml(parser, name, callback, arrayMap);
917                list.add(val);
918                //System.out.println("Adding to list: " + val);
919            } else if (eventType == parser.END_TAG) {
920                if (parser.getName().equals(endTag)) {
921                    return list;
922                }
923                throw new XmlPullParserException(
924                    "Expected " + endTag + " end tag at: " + parser.getName());
925            }
926            eventType = parser.next();
927        } while (eventType != parser.END_DOCUMENT);
928
929        throw new XmlPullParserException(
930            "Document ended before " + endTag + " end tag");
931    }
932
933    /**
934     * Read a HashSet object from an XmlPullParser. The XML data could previously
935     * have been generated by writeSetXml(). The XmlPullParser must be positioned
936     * <em>after</em> the tag that begins the set.
937     *
938     * @param parser The XmlPullParser from which to read the set data.
939     * @param endTag Name of the tag that will end the set, usually "set".
940     * @param name An array of one string, used to return the name attribute
941     *             of the set's tag.
942     *
943     * @return HashSet The newly generated set.
944     *
945     * @throws XmlPullParserException
946     * @throws java.io.IOException
947     *
948     * @see #readSetXml
949     */
950    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
951            throws XmlPullParserException, java.io.IOException {
952        return readThisSetXml(parser, endTag, name, null, false);
953    }
954
955    /**
956     * Read a HashSet object from an XmlPullParser. The XML data could previously
957     * have been generated by writeSetXml(). The XmlPullParser must be positioned
958     * <em>after</em> the tag that begins the set.
959     *
960     * @param parser The XmlPullParser from which to read the set data.
961     * @param endTag Name of the tag that will end the set, usually "set".
962     * @param name An array of one string, used to return the name attribute
963     *             of the set's tag.
964     *
965     * @return HashSet The newly generated set.
966     *
967     * @throws XmlPullParserException
968     * @throws java.io.IOException
969     *
970     * @see #readSetXml
971     * @hide
972     */
973    private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
974            ReadMapCallback callback, boolean arrayMap)
975            throws XmlPullParserException, java.io.IOException {
976        HashSet set = new HashSet();
977
978        int eventType = parser.getEventType();
979        do {
980            if (eventType == parser.START_TAG) {
981                Object val = readThisValueXml(parser, name, callback, arrayMap);
982                set.add(val);
983                //System.out.println("Adding to set: " + val);
984            } else if (eventType == parser.END_TAG) {
985                if (parser.getName().equals(endTag)) {
986                    return set;
987                }
988                throw new XmlPullParserException(
989                        "Expected " + endTag + " end tag at: " + parser.getName());
990            }
991            eventType = parser.next();
992        } while (eventType != parser.END_DOCUMENT);
993
994        throw new XmlPullParserException(
995                "Document ended before " + endTag + " end tag");
996    }
997
998    /**
999     * Read an int[] object from an XmlPullParser.  The XML data could
1000     * previously have been generated by writeIntArrayXml().  The XmlPullParser
1001     * must be positioned <em>after</em> the tag that begins the list.
1002     *
1003     * @param parser The XmlPullParser from which to read the list data.
1004     * @param endTag Name of the tag that will end the list, usually "list".
1005     * @param name An array of one string, used to return the name attribute
1006     *             of the list's tag.
1007     *
1008     * @return Returns a newly generated int[].
1009     *
1010     * @see #readListXml
1011     */
1012    public static final int[] readThisIntArrayXml(XmlPullParser parser,
1013            String endTag, String[] name)
1014            throws XmlPullParserException, java.io.IOException {
1015
1016        int num;
1017        try {
1018            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1019        } catch (NullPointerException e) {
1020            throw new XmlPullParserException(
1021                    "Need num attribute in byte-array");
1022        } catch (NumberFormatException e) {
1023            throw new XmlPullParserException(
1024                    "Not a number in num attribute in byte-array");
1025        }
1026        parser.next();
1027
1028        int[] array = new int[num];
1029        int i = 0;
1030
1031        int eventType = parser.getEventType();
1032        do {
1033            if (eventType == parser.START_TAG) {
1034                if (parser.getName().equals("item")) {
1035                    try {
1036                        array[i] = Integer.parseInt(
1037                                parser.getAttributeValue(null, "value"));
1038                    } catch (NullPointerException e) {
1039                        throw new XmlPullParserException(
1040                                "Need value attribute in item");
1041                    } catch (NumberFormatException e) {
1042                        throw new XmlPullParserException(
1043                                "Not a number in value attribute in item");
1044                    }
1045                } else {
1046                    throw new XmlPullParserException(
1047                            "Expected item tag at: " + parser.getName());
1048                }
1049            } else if (eventType == parser.END_TAG) {
1050                if (parser.getName().equals(endTag)) {
1051                    return array;
1052                } else if (parser.getName().equals("item")) {
1053                    i++;
1054                } else {
1055                    throw new XmlPullParserException(
1056                        "Expected " + endTag + " end tag at: "
1057                        + parser.getName());
1058                }
1059            }
1060            eventType = parser.next();
1061        } while (eventType != parser.END_DOCUMENT);
1062
1063        throw new XmlPullParserException(
1064            "Document ended before " + endTag + " end tag");
1065    }
1066
1067    /**
1068     * Read a long[] object from an XmlPullParser.  The XML data could
1069     * previously have been generated by writeLongArrayXml().  The XmlPullParser
1070     * must be positioned <em>after</em> the tag that begins the list.
1071     *
1072     * @param parser The XmlPullParser from which to read the list data.
1073     * @param endTag Name of the tag that will end the list, usually "list".
1074     * @param name An array of one string, used to return the name attribute
1075     *             of the list's tag.
1076     *
1077     * @return Returns a newly generated long[].
1078     *
1079     * @see #readListXml
1080     */
1081    public static final long[] readThisLongArrayXml(XmlPullParser parser,
1082            String endTag, String[] name)
1083            throws XmlPullParserException, java.io.IOException {
1084
1085        int num;
1086        try {
1087            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1088        } catch (NullPointerException e) {
1089            throw new XmlPullParserException("Need num attribute in long-array");
1090        } catch (NumberFormatException e) {
1091            throw new XmlPullParserException("Not a number in num attribute in long-array");
1092        }
1093        parser.next();
1094
1095        long[] array = new long[num];
1096        int i = 0;
1097
1098        int eventType = parser.getEventType();
1099        do {
1100            if (eventType == parser.START_TAG) {
1101                if (parser.getName().equals("item")) {
1102                    try {
1103                        array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
1104                    } catch (NullPointerException e) {
1105                        throw new XmlPullParserException("Need value attribute in item");
1106                    } catch (NumberFormatException e) {
1107                        throw new XmlPullParserException("Not a number in value attribute in item");
1108                    }
1109                } else {
1110                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1111                }
1112            } else if (eventType == parser.END_TAG) {
1113                if (parser.getName().equals(endTag)) {
1114                    return array;
1115                } else if (parser.getName().equals("item")) {
1116                    i++;
1117                } else {
1118                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1119                            parser.getName());
1120                }
1121            }
1122            eventType = parser.next();
1123        } while (eventType != parser.END_DOCUMENT);
1124
1125        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1126    }
1127
1128    /**
1129     * Read a double[] object from an XmlPullParser.  The XML data could
1130     * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
1131     * must be positioned <em>after</em> the tag that begins the list.
1132     *
1133     * @param parser The XmlPullParser from which to read the list data.
1134     * @param endTag Name of the tag that will end the list, usually "double-array".
1135     * @param name An array of one string, used to return the name attribute
1136     *             of the list's tag.
1137     *
1138     * @return Returns a newly generated double[].
1139     *
1140     * @see #readListXml
1141     */
1142    public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
1143            String[] name) throws XmlPullParserException, java.io.IOException {
1144
1145        int num;
1146        try {
1147            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1148        } catch (NullPointerException e) {
1149            throw new XmlPullParserException("Need num attribute in double-array");
1150        } catch (NumberFormatException e) {
1151            throw new XmlPullParserException("Not a number in num attribute in double-array");
1152        }
1153        parser.next();
1154
1155        double[] array = new double[num];
1156        int i = 0;
1157
1158        int eventType = parser.getEventType();
1159        do {
1160            if (eventType == parser.START_TAG) {
1161                if (parser.getName().equals("item")) {
1162                    try {
1163                        array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
1164                    } catch (NullPointerException e) {
1165                        throw new XmlPullParserException("Need value attribute in item");
1166                    } catch (NumberFormatException e) {
1167                        throw new XmlPullParserException("Not a number in value attribute in item");
1168                    }
1169                } else {
1170                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1171                }
1172            } else if (eventType == parser.END_TAG) {
1173                if (parser.getName().equals(endTag)) {
1174                    return array;
1175                } else if (parser.getName().equals("item")) {
1176                    i++;
1177                } else {
1178                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1179                            parser.getName());
1180                }
1181            }
1182            eventType = parser.next();
1183        } while (eventType != parser.END_DOCUMENT);
1184
1185        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1186    }
1187
1188    /**
1189     * Read a String[] object from an XmlPullParser.  The XML data could
1190     * previously have been generated by writeStringArrayXml().  The XmlPullParser
1191     * must be positioned <em>after</em> the tag that begins the list.
1192     *
1193     * @param parser The XmlPullParser from which to read the list data.
1194     * @param endTag Name of the tag that will end the list, usually "string-array".
1195     * @param name An array of one string, used to return the name attribute
1196     *             of the list's tag.
1197     *
1198     * @return Returns a newly generated String[].
1199     *
1200     * @see #readListXml
1201     */
1202    public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
1203            String[] name) throws XmlPullParserException, java.io.IOException {
1204
1205        int num;
1206        try {
1207            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1208        } catch (NullPointerException e) {
1209            throw new XmlPullParserException("Need num attribute in string-array");
1210        } catch (NumberFormatException e) {
1211            throw new XmlPullParserException("Not a number in num attribute in string-array");
1212        }
1213        parser.next();
1214
1215        String[] array = new String[num];
1216        int i = 0;
1217
1218        int eventType = parser.getEventType();
1219        do {
1220            if (eventType == parser.START_TAG) {
1221                if (parser.getName().equals("item")) {
1222                    try {
1223                        array[i] = parser.getAttributeValue(null, "value");
1224                    } catch (NullPointerException e) {
1225                        throw new XmlPullParserException("Need value attribute in item");
1226                    } catch (NumberFormatException e) {
1227                        throw new XmlPullParserException("Not a number in value attribute in item");
1228                    }
1229                } else {
1230                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1231                }
1232            } else if (eventType == parser.END_TAG) {
1233                if (parser.getName().equals(endTag)) {
1234                    return array;
1235                } else if (parser.getName().equals("item")) {
1236                    i++;
1237                } else {
1238                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1239                            parser.getName());
1240                }
1241            }
1242            eventType = parser.next();
1243        } while (eventType != parser.END_DOCUMENT);
1244
1245        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1246    }
1247
1248    /**
1249     * Read a boolean[] object from an XmlPullParser.  The XML data could
1250     * previously have been generated by writeBooleanArrayXml().  The XmlPullParser
1251     * must be positioned <em>after</em> the tag that begins the list.
1252     *
1253     * @param parser The XmlPullParser from which to read the list data.
1254     * @param endTag Name of the tag that will end the list, usually "string-array".
1255     * @param name An array of one string, used to return the name attribute
1256     *             of the list's tag.
1257     *
1258     * @return Returns a newly generated boolean[].
1259     *
1260     * @see #readListXml
1261     */
1262    public static final boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag,
1263            String[] name) throws XmlPullParserException, java.io.IOException {
1264
1265        int num;
1266        try {
1267            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1268        } catch (NullPointerException e) {
1269            throw new XmlPullParserException("Need num attribute in string-array");
1270        } catch (NumberFormatException e) {
1271            throw new XmlPullParserException("Not a number in num attribute in string-array");
1272        }
1273        parser.next();
1274
1275        boolean[] array = new boolean[num];
1276        int i = 0;
1277
1278        int eventType = parser.getEventType();
1279        do {
1280            if (eventType == parser.START_TAG) {
1281                if (parser.getName().equals("item")) {
1282                    try {
1283                        array[i] = Boolean.valueOf(parser.getAttributeValue(null, "value"));
1284                    } catch (NullPointerException e) {
1285                        throw new XmlPullParserException("Need value attribute in item");
1286                    } catch (NumberFormatException e) {
1287                        throw new XmlPullParserException("Not a number in value attribute in item");
1288                    }
1289                } else {
1290                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1291                }
1292            } else if (eventType == parser.END_TAG) {
1293                if (parser.getName().equals(endTag)) {
1294                    return array;
1295                } else if (parser.getName().equals("item")) {
1296                    i++;
1297                } else {
1298                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1299                            parser.getName());
1300                }
1301            }
1302            eventType = parser.next();
1303        } while (eventType != parser.END_DOCUMENT);
1304
1305        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1306    }
1307
1308    /**
1309     * Read a flattened object from an XmlPullParser.  The XML data could
1310     * previously have been written with writeMapXml(), writeListXml(), or
1311     * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
1312     * tag that defines the value.
1313     *
1314     * @param parser The XmlPullParser from which to read the object.
1315     * @param name An array of one string, used to return the name attribute
1316     *             of the value's tag.
1317     *
1318     * @return Object The newly generated value object.
1319     *
1320     * @see #readMapXml
1321     * @see #readListXml
1322     * @see #writeValueXml
1323     */
1324    public static final Object readValueXml(XmlPullParser parser, String[] name)
1325    throws XmlPullParserException, java.io.IOException
1326    {
1327        int eventType = parser.getEventType();
1328        do {
1329            if (eventType == parser.START_TAG) {
1330                return readThisValueXml(parser, name, null, false);
1331            } else if (eventType == parser.END_TAG) {
1332                throw new XmlPullParserException(
1333                    "Unexpected end tag at: " + parser.getName());
1334            } else if (eventType == parser.TEXT) {
1335                throw new XmlPullParserException(
1336                    "Unexpected text: " + parser.getText());
1337            }
1338            eventType = parser.next();
1339        } while (eventType != parser.END_DOCUMENT);
1340
1341        throw new XmlPullParserException(
1342            "Unexpected end of document");
1343    }
1344
1345    private static final Object readThisValueXml(XmlPullParser parser, String[] name,
1346            ReadMapCallback callback, boolean arrayMap)
1347            throws XmlPullParserException, java.io.IOException {
1348        final String valueName = parser.getAttributeValue(null, "name");
1349        final String tagName = parser.getName();
1350
1351        //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1352
1353        Object res;
1354
1355        if (tagName.equals("null")) {
1356            res = null;
1357        } else if (tagName.equals("string")) {
1358            String value = "";
1359            int eventType;
1360            while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1361                if (eventType == parser.END_TAG) {
1362                    if (parser.getName().equals("string")) {
1363                        name[0] = valueName;
1364                        //System.out.println("Returning value for " + valueName + ": " + value);
1365                        return value;
1366                    }
1367                    throw new XmlPullParserException(
1368                        "Unexpected end tag in <string>: " + parser.getName());
1369                } else if (eventType == parser.TEXT) {
1370                    value += parser.getText();
1371                } else if (eventType == parser.START_TAG) {
1372                    throw new XmlPullParserException(
1373                        "Unexpected start tag in <string>: " + parser.getName());
1374                }
1375            }
1376            throw new XmlPullParserException(
1377                "Unexpected end of document in <string>");
1378        } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1379            // all work already done by readThisPrimitiveValueXml
1380        } else if (tagName.equals("int-array")) {
1381            res = readThisIntArrayXml(parser, "int-array", name);
1382            name[0] = valueName;
1383            //System.out.println("Returning value for " + valueName + ": " + res);
1384            return res;
1385        } else if (tagName.equals("long-array")) {
1386            res = readThisLongArrayXml(parser, "long-array", name);
1387            name[0] = valueName;
1388            //System.out.println("Returning value for " + valueName + ": " + res);
1389            return res;
1390        } else if (tagName.equals("double-array")) {
1391            res = readThisDoubleArrayXml(parser, "double-array", name);
1392            name[0] = valueName;
1393            //System.out.println("Returning value for " + valueName + ": " + res);
1394            return res;
1395        } else if (tagName.equals("string-array")) {
1396            res = readThisStringArrayXml(parser, "string-array", name);
1397            name[0] = valueName;
1398            //System.out.println("Returning value for " + valueName + ": " + res);
1399            return res;
1400        } else if (tagName.equals("boolean-array")) {
1401            res = readThisBooleanArrayXml(parser, "boolean-array", name);
1402            name[0] = valueName;
1403            //System.out.println("Returning value for " + valueName + ": " + res);
1404            return res;
1405        } else if (tagName.equals("map")) {
1406            parser.next();
1407            res = arrayMap
1408                    ? readThisArrayMapXml(parser, "map", name, callback)
1409                    : readThisMapXml(parser, "map", name, callback);
1410            name[0] = valueName;
1411            //System.out.println("Returning value for " + valueName + ": " + res);
1412            return res;
1413        } else if (tagName.equals("list")) {
1414            parser.next();
1415            res = readThisListXml(parser, "list", name, callback, arrayMap);
1416            name[0] = valueName;
1417            //System.out.println("Returning value for " + valueName + ": " + res);
1418            return res;
1419        } else if (tagName.equals("set")) {
1420            parser.next();
1421            res = readThisSetXml(parser, "set", name, callback, arrayMap);
1422            name[0] = valueName;
1423            //System.out.println("Returning value for " + valueName + ": " + res);
1424            return res;
1425        } else if (callback != null) {
1426            res = callback.readThisUnknownObjectXml(parser, tagName);
1427            name[0] = valueName;
1428            return res;
1429        } else {
1430            throw new XmlPullParserException("Unknown tag: " + tagName);
1431        }
1432
1433        // Skip through to end tag.
1434        int eventType;
1435        while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1436            if (eventType == parser.END_TAG) {
1437                if (parser.getName().equals(tagName)) {
1438                    name[0] = valueName;
1439                    //System.out.println("Returning value for " + valueName + ": " + res);
1440                    return res;
1441                }
1442                throw new XmlPullParserException(
1443                    "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1444            } else if (eventType == parser.TEXT) {
1445                throw new XmlPullParserException(
1446                "Unexpected text in <" + tagName + ">: " + parser.getName());
1447            } else if (eventType == parser.START_TAG) {
1448                throw new XmlPullParserException(
1449                    "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1450            }
1451        }
1452        throw new XmlPullParserException(
1453            "Unexpected end of document in <" + tagName + ">");
1454    }
1455
1456    private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
1457    throws XmlPullParserException, java.io.IOException
1458    {
1459        try {
1460            if (tagName.equals("int")) {
1461                return Integer.parseInt(parser.getAttributeValue(null, "value"));
1462            } else if (tagName.equals("long")) {
1463                return Long.valueOf(parser.getAttributeValue(null, "value"));
1464            } else if (tagName.equals("float")) {
1465                return new Float(parser.getAttributeValue(null, "value"));
1466            } else if (tagName.equals("double")) {
1467                return new Double(parser.getAttributeValue(null, "value"));
1468            } else if (tagName.equals("boolean")) {
1469                return Boolean.valueOf(parser.getAttributeValue(null, "value"));
1470            } else {
1471                return null;
1472            }
1473        } catch (NullPointerException e) {
1474            throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
1475        } catch (NumberFormatException e) {
1476            throw new XmlPullParserException(
1477                    "Not a number in value attribute in <" + tagName + ">");
1478        }
1479    }
1480
1481    public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1482    {
1483        int type;
1484        while ((type=parser.next()) != parser.START_TAG
1485                   && type != parser.END_DOCUMENT) {
1486            ;
1487        }
1488
1489        if (type != parser.START_TAG) {
1490            throw new XmlPullParserException("No start tag found");
1491        }
1492
1493        if (!parser.getName().equals(firstElementName)) {
1494            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1495                    ", expected " + firstElementName);
1496        }
1497    }
1498
1499    public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1500    {
1501        int type;
1502        while ((type=parser.next()) != parser.START_TAG
1503                   && type != parser.END_DOCUMENT) {
1504            ;
1505        }
1506    }
1507
1508    public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1509            throws IOException, XmlPullParserException {
1510        for (;;) {
1511            int type = parser.next();
1512            if (type == XmlPullParser.END_DOCUMENT
1513                    || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1514                return false;
1515            }
1516            if (type == XmlPullParser.START_TAG
1517                    && parser.getDepth() == outerDepth + 1) {
1518                return true;
1519            }
1520        }
1521    }
1522
1523    public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1524        final String value = in.getAttributeValue(null, name);
1525        try {
1526            return Integer.parseInt(value);
1527        } catch (NumberFormatException e) {
1528            return defaultValue;
1529        }
1530    }
1531
1532    public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1533        final String value = in.getAttributeValue(null, name);
1534        try {
1535            return Integer.parseInt(value);
1536        } catch (NumberFormatException e) {
1537            throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1538        }
1539    }
1540
1541    public static void writeIntAttribute(XmlSerializer out, String name, int value)
1542            throws IOException {
1543        out.attribute(null, name, Integer.toString(value));
1544    }
1545
1546    public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1547        final String value = in.getAttributeValue(null, name);
1548        try {
1549            return Long.parseLong(value);
1550        } catch (NumberFormatException e) {
1551            return defaultValue;
1552        }
1553    }
1554
1555    public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1556        final String value = in.getAttributeValue(null, name);
1557        try {
1558            return Long.parseLong(value);
1559        } catch (NumberFormatException e) {
1560            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1561        }
1562    }
1563
1564    public static void writeLongAttribute(XmlSerializer out, String name, long value)
1565            throws IOException {
1566        out.attribute(null, name, Long.toString(value));
1567    }
1568
1569    public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
1570        final String value = in.getAttributeValue(null, name);
1571        try {
1572            return Float.parseFloat(value);
1573        } catch (NumberFormatException e) {
1574            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1575        }
1576    }
1577
1578    public static void writeFloatAttribute(XmlSerializer out, String name, float value)
1579            throws IOException {
1580        out.attribute(null, name, Float.toString(value));
1581    }
1582
1583    public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1584        final String value = in.getAttributeValue(null, name);
1585        return Boolean.parseBoolean(value);
1586    }
1587
1588    public static boolean readBooleanAttribute(XmlPullParser in, String name,
1589            boolean defaultValue) {
1590        final String value = in.getAttributeValue(null, name);
1591        if (value == null) {
1592            return defaultValue;
1593        } else {
1594            return Boolean.parseBoolean(value);
1595        }
1596    }
1597
1598    public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1599            throws IOException {
1600        out.attribute(null, name, Boolean.toString(value));
1601    }
1602
1603    public static Uri readUriAttribute(XmlPullParser in, String name) {
1604        final String value = in.getAttributeValue(null, name);
1605        return (value != null) ? Uri.parse(value) : null;
1606    }
1607
1608    public static void writeUriAttribute(XmlSerializer out, String name, Uri value)
1609            throws IOException {
1610        if (value != null) {
1611            out.attribute(null, name, value.toString());
1612        }
1613    }
1614
1615    public static String readStringAttribute(XmlPullParser in, String name) {
1616        return in.getAttributeValue(null, name);
1617    }
1618
1619    public static void writeStringAttribute(XmlSerializer out, String name, String value)
1620            throws IOException {
1621        if (value != null) {
1622            out.attribute(null, name, value);
1623        }
1624    }
1625
1626    public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
1627        final String value = in.getAttributeValue(null, name);
1628        if (value != null) {
1629            return Base64.decode(value, Base64.DEFAULT);
1630        } else {
1631            return null;
1632        }
1633    }
1634
1635    public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
1636            throws IOException {
1637        if (value != null) {
1638            out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT));
1639        }
1640    }
1641
1642    public static Bitmap readBitmapAttribute(XmlPullParser in, String name) {
1643        final byte[] value = readByteArrayAttribute(in, name);
1644        if (value != null) {
1645            return BitmapFactory.decodeByteArray(value, 0, value.length);
1646        } else {
1647            return null;
1648        }
1649    }
1650
1651    @Deprecated
1652    public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
1653            throws IOException {
1654        if (value != null) {
1655            final ByteArrayOutputStream os = new ByteArrayOutputStream();
1656            value.compress(CompressFormat.PNG, 90, os);
1657            writeByteArrayAttribute(out, name, os.toByteArray());
1658        }
1659    }
1660
1661    /** @hide */
1662    public interface WriteMapCallback {
1663        /**
1664         * Called from writeMapXml when an Object type is not recognized. The implementer
1665         * must write out the entire element including start and end tags.
1666         *
1667         * @param v The object to be written out
1668         * @param name The mapping key for v. Must be written into the "name" attribute of the
1669         *             start tag.
1670         * @param out The XML output stream.
1671         * @throws XmlPullParserException on unrecognized Object type.
1672         * @throws IOException on XmlSerializer serialization errors.
1673         * @hide
1674         */
1675         public void writeUnknownObject(Object v, String name, XmlSerializer out)
1676                 throws XmlPullParserException, IOException;
1677    }
1678
1679    /** @hide */
1680    public interface ReadMapCallback {
1681        /**
1682         * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1683         * is positioned within the start tag so that attributes can be read using in.getAttribute.
1684         *
1685         * @param in the XML input stream
1686         * @param tag the START_TAG that was not recognized.
1687         * @return the Object parsed from the stream which will be put into the map.
1688         * @throws XmlPullParserException if the START_TAG is not recognized.
1689         * @throws IOException on XmlPullParser serialization errors.
1690         * @hide
1691         */
1692        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
1693                throws XmlPullParserException, IOException;
1694    }
1695}
1696