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