XmlUtils.java revision 651209b597734d34768fbb69ebf982b483167fd5
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) & 0x0f;
396            sb.append((char)(h >= 10 ? ('a'+h-10) : ('0'+h)));
397            h = b & 0x0f;
398            sb.append((char)(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 a byte[] object from an XmlPullParser.  The XML data could
1000     * previously have been generated by writeByteArrayXml().  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 byte[].
1009     *
1010     * @see #writeByteArrayXml
1011     */
1012    public static final byte[] readThisByteArrayXml(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
1027        byte[] array = new byte[num];
1028
1029        int eventType = parser.getEventType();
1030        do {
1031            if (eventType == parser.TEXT) {
1032                if (num > 0) {
1033                    String values = parser.getText();
1034                    if (values == null || values.length() != num * 2) {
1035                        throw new XmlPullParserException(
1036                                "Invalid value found in byte-array: " + values);
1037                    }
1038                    // This is ugly, but keeping it to mirror the logic in #writeByteArrayXml.
1039                    for (int i = 0; i < num; i ++) {
1040                        char nibbleHighChar = values.charAt(2 * i);
1041                        char nibbleLowChar = values.charAt(2 * i + 1);
1042                        int nibbleHigh = nibbleHighChar > 'a' ? (nibbleHighChar - 'a' + 10)
1043                                : (nibbleHighChar - '0');
1044                        int nibbleLow = nibbleLowChar > 'a' ? (nibbleLowChar - 'a' + 10)
1045                                : (nibbleLowChar - '0');
1046                        array[i] = (byte) ((nibbleHigh & 0x0F) << 4 | (nibbleLow & 0x0F));
1047                    }
1048                }
1049            } else if (eventType == parser.END_TAG) {
1050                if (parser.getName().equals(endTag)) {
1051                    return array;
1052                } else {
1053                    throw new XmlPullParserException(
1054                            "Expected " + endTag + " end tag at: "
1055                                    + parser.getName());
1056                }
1057            }
1058            eventType = parser.next();
1059        } while (eventType != parser.END_DOCUMENT);
1060
1061        throw new XmlPullParserException(
1062                "Document ended before " + endTag + " end tag");
1063    }
1064
1065    /**
1066     * Read an int[] object from an XmlPullParser.  The XML data could
1067     * previously have been generated by writeIntArrayXml().  The XmlPullParser
1068     * must be positioned <em>after</em> the tag that begins the list.
1069     *
1070     * @param parser The XmlPullParser from which to read the list data.
1071     * @param endTag Name of the tag that will end the list, usually "list".
1072     * @param name An array of one string, used to return the name attribute
1073     *             of the list's tag.
1074     *
1075     * @return Returns a newly generated int[].
1076     *
1077     * @see #readListXml
1078     */
1079    public static final int[] readThisIntArrayXml(XmlPullParser parser,
1080            String endTag, String[] name)
1081            throws XmlPullParserException, java.io.IOException {
1082
1083        int num;
1084        try {
1085            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1086        } catch (NullPointerException e) {
1087            throw new XmlPullParserException(
1088                    "Need num attribute in int-array");
1089        } catch (NumberFormatException e) {
1090            throw new XmlPullParserException(
1091                    "Not a number in num attribute in int-array");
1092        }
1093        parser.next();
1094
1095        int[] array = new int[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] = Integer.parseInt(
1104                                parser.getAttributeValue(null, "value"));
1105                    } catch (NullPointerException e) {
1106                        throw new XmlPullParserException(
1107                                "Need value attribute in item");
1108                    } catch (NumberFormatException e) {
1109                        throw new XmlPullParserException(
1110                                "Not a number in value attribute in item");
1111                    }
1112                } else {
1113                    throw new XmlPullParserException(
1114                            "Expected item tag at: " + parser.getName());
1115                }
1116            } else if (eventType == parser.END_TAG) {
1117                if (parser.getName().equals(endTag)) {
1118                    return array;
1119                } else if (parser.getName().equals("item")) {
1120                    i++;
1121                } else {
1122                    throw new XmlPullParserException(
1123                        "Expected " + endTag + " end tag at: "
1124                        + parser.getName());
1125                }
1126            }
1127            eventType = parser.next();
1128        } while (eventType != parser.END_DOCUMENT);
1129
1130        throw new XmlPullParserException(
1131            "Document ended before " + endTag + " end tag");
1132    }
1133
1134    /**
1135     * Read a long[] object from an XmlPullParser.  The XML data could
1136     * previously have been generated by writeLongArrayXml().  The XmlPullParser
1137     * must be positioned <em>after</em> the tag that begins the list.
1138     *
1139     * @param parser The XmlPullParser from which to read the list data.
1140     * @param endTag Name of the tag that will end the list, usually "list".
1141     * @param name An array of one string, used to return the name attribute
1142     *             of the list's tag.
1143     *
1144     * @return Returns a newly generated long[].
1145     *
1146     * @see #readListXml
1147     */
1148    public static final long[] readThisLongArrayXml(XmlPullParser parser,
1149            String endTag, String[] name)
1150            throws XmlPullParserException, java.io.IOException {
1151
1152        int num;
1153        try {
1154            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1155        } catch (NullPointerException e) {
1156            throw new XmlPullParserException("Need num attribute in long-array");
1157        } catch (NumberFormatException e) {
1158            throw new XmlPullParserException("Not a number in num attribute in long-array");
1159        }
1160        parser.next();
1161
1162        long[] array = new long[num];
1163        int i = 0;
1164
1165        int eventType = parser.getEventType();
1166        do {
1167            if (eventType == parser.START_TAG) {
1168                if (parser.getName().equals("item")) {
1169                    try {
1170                        array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
1171                    } catch (NullPointerException e) {
1172                        throw new XmlPullParserException("Need value attribute in item");
1173                    } catch (NumberFormatException e) {
1174                        throw new XmlPullParserException("Not a number in value attribute in item");
1175                    }
1176                } else {
1177                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1178                }
1179            } else if (eventType == parser.END_TAG) {
1180                if (parser.getName().equals(endTag)) {
1181                    return array;
1182                } else if (parser.getName().equals("item")) {
1183                    i++;
1184                } else {
1185                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1186                            parser.getName());
1187                }
1188            }
1189            eventType = parser.next();
1190        } while (eventType != parser.END_DOCUMENT);
1191
1192        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1193    }
1194
1195    /**
1196     * Read a double[] object from an XmlPullParser.  The XML data could
1197     * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
1198     * must be positioned <em>after</em> the tag that begins the list.
1199     *
1200     * @param parser The XmlPullParser from which to read the list data.
1201     * @param endTag Name of the tag that will end the list, usually "double-array".
1202     * @param name An array of one string, used to return the name attribute
1203     *             of the list's tag.
1204     *
1205     * @return Returns a newly generated double[].
1206     *
1207     * @see #readListXml
1208     */
1209    public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
1210            String[] name) throws XmlPullParserException, java.io.IOException {
1211
1212        int num;
1213        try {
1214            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1215        } catch (NullPointerException e) {
1216            throw new XmlPullParserException("Need num attribute in double-array");
1217        } catch (NumberFormatException e) {
1218            throw new XmlPullParserException("Not a number in num attribute in double-array");
1219        }
1220        parser.next();
1221
1222        double[] array = new double[num];
1223        int i = 0;
1224
1225        int eventType = parser.getEventType();
1226        do {
1227            if (eventType == parser.START_TAG) {
1228                if (parser.getName().equals("item")) {
1229                    try {
1230                        array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
1231                    } catch (NullPointerException e) {
1232                        throw new XmlPullParserException("Need value attribute in item");
1233                    } catch (NumberFormatException e) {
1234                        throw new XmlPullParserException("Not a number in value attribute in item");
1235                    }
1236                } else {
1237                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1238                }
1239            } else if (eventType == parser.END_TAG) {
1240                if (parser.getName().equals(endTag)) {
1241                    return array;
1242                } else if (parser.getName().equals("item")) {
1243                    i++;
1244                } else {
1245                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1246                            parser.getName());
1247                }
1248            }
1249            eventType = parser.next();
1250        } while (eventType != parser.END_DOCUMENT);
1251
1252        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1253    }
1254
1255    /**
1256     * Read a String[] object from an XmlPullParser.  The XML data could
1257     * previously have been generated by writeStringArrayXml().  The XmlPullParser
1258     * must be positioned <em>after</em> the tag that begins the list.
1259     *
1260     * @param parser The XmlPullParser from which to read the list data.
1261     * @param endTag Name of the tag that will end the list, usually "string-array".
1262     * @param name An array of one string, used to return the name attribute
1263     *             of the list's tag.
1264     *
1265     * @return Returns a newly generated String[].
1266     *
1267     * @see #readListXml
1268     */
1269    public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
1270            String[] name) throws XmlPullParserException, java.io.IOException {
1271
1272        int num;
1273        try {
1274            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1275        } catch (NullPointerException e) {
1276            throw new XmlPullParserException("Need num attribute in string-array");
1277        } catch (NumberFormatException e) {
1278            throw new XmlPullParserException("Not a number in num attribute in string-array");
1279        }
1280        parser.next();
1281
1282        String[] array = new String[num];
1283        int i = 0;
1284
1285        int eventType = parser.getEventType();
1286        do {
1287            if (eventType == parser.START_TAG) {
1288                if (parser.getName().equals("item")) {
1289                    try {
1290                        array[i] = parser.getAttributeValue(null, "value");
1291                    } catch (NullPointerException e) {
1292                        throw new XmlPullParserException("Need value attribute in item");
1293                    } catch (NumberFormatException e) {
1294                        throw new XmlPullParserException("Not a number in value attribute in item");
1295                    }
1296                } else {
1297                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1298                }
1299            } else if (eventType == parser.END_TAG) {
1300                if (parser.getName().equals(endTag)) {
1301                    return array;
1302                } else if (parser.getName().equals("item")) {
1303                    i++;
1304                } else {
1305                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1306                            parser.getName());
1307                }
1308            }
1309            eventType = parser.next();
1310        } while (eventType != parser.END_DOCUMENT);
1311
1312        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1313    }
1314
1315    /**
1316     * Read a boolean[] object from an XmlPullParser.  The XML data could
1317     * previously have been generated by writeBooleanArrayXml().  The XmlPullParser
1318     * must be positioned <em>after</em> the tag that begins the list.
1319     *
1320     * @param parser The XmlPullParser from which to read the list data.
1321     * @param endTag Name of the tag that will end the list, usually "string-array".
1322     * @param name An array of one string, used to return the name attribute
1323     *             of the list's tag.
1324     *
1325     * @return Returns a newly generated boolean[].
1326     *
1327     * @see #readListXml
1328     */
1329    public static final boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag,
1330            String[] name) throws XmlPullParserException, java.io.IOException {
1331
1332        int num;
1333        try {
1334            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1335        } catch (NullPointerException e) {
1336            throw new XmlPullParserException("Need num attribute in string-array");
1337        } catch (NumberFormatException e) {
1338            throw new XmlPullParserException("Not a number in num attribute in string-array");
1339        }
1340        parser.next();
1341
1342        boolean[] array = new boolean[num];
1343        int i = 0;
1344
1345        int eventType = parser.getEventType();
1346        do {
1347            if (eventType == parser.START_TAG) {
1348                if (parser.getName().equals("item")) {
1349                    try {
1350                        array[i] = Boolean.parseBoolean(parser.getAttributeValue(null, "value"));
1351                    } catch (NullPointerException e) {
1352                        throw new XmlPullParserException("Need value attribute in item");
1353                    } catch (NumberFormatException e) {
1354                        throw new XmlPullParserException("Not a number in value attribute in item");
1355                    }
1356                } else {
1357                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1358                }
1359            } else if (eventType == parser.END_TAG) {
1360                if (parser.getName().equals(endTag)) {
1361                    return array;
1362                } else if (parser.getName().equals("item")) {
1363                    i++;
1364                } else {
1365                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1366                            parser.getName());
1367                }
1368            }
1369            eventType = parser.next();
1370        } while (eventType != parser.END_DOCUMENT);
1371
1372        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1373    }
1374
1375    /**
1376     * Read a flattened object from an XmlPullParser.  The XML data could
1377     * previously have been written with writeMapXml(), writeListXml(), or
1378     * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
1379     * tag that defines the value.
1380     *
1381     * @param parser The XmlPullParser from which to read the object.
1382     * @param name An array of one string, used to return the name attribute
1383     *             of the value's tag.
1384     *
1385     * @return Object The newly generated value object.
1386     *
1387     * @see #readMapXml
1388     * @see #readListXml
1389     * @see #writeValueXml
1390     */
1391    public static final Object readValueXml(XmlPullParser parser, String[] name)
1392    throws XmlPullParserException, java.io.IOException
1393    {
1394        int eventType = parser.getEventType();
1395        do {
1396            if (eventType == parser.START_TAG) {
1397                return readThisValueXml(parser, name, null, false);
1398            } else if (eventType == parser.END_TAG) {
1399                throw new XmlPullParserException(
1400                    "Unexpected end tag at: " + parser.getName());
1401            } else if (eventType == parser.TEXT) {
1402                throw new XmlPullParserException(
1403                    "Unexpected text: " + parser.getText());
1404            }
1405            eventType = parser.next();
1406        } while (eventType != parser.END_DOCUMENT);
1407
1408        throw new XmlPullParserException(
1409            "Unexpected end of document");
1410    }
1411
1412    private static final Object readThisValueXml(XmlPullParser parser, String[] name,
1413            ReadMapCallback callback, boolean arrayMap)
1414            throws XmlPullParserException, java.io.IOException {
1415        final String valueName = parser.getAttributeValue(null, "name");
1416        final String tagName = parser.getName();
1417
1418        //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1419
1420        Object res;
1421
1422        if (tagName.equals("null")) {
1423            res = null;
1424        } else if (tagName.equals("string")) {
1425            String value = "";
1426            int eventType;
1427            while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1428                if (eventType == parser.END_TAG) {
1429                    if (parser.getName().equals("string")) {
1430                        name[0] = valueName;
1431                        //System.out.println("Returning value for " + valueName + ": " + value);
1432                        return value;
1433                    }
1434                    throw new XmlPullParserException(
1435                        "Unexpected end tag in <string>: " + parser.getName());
1436                } else if (eventType == parser.TEXT) {
1437                    value += parser.getText();
1438                } else if (eventType == parser.START_TAG) {
1439                    throw new XmlPullParserException(
1440                        "Unexpected start tag in <string>: " + parser.getName());
1441                }
1442            }
1443            throw new XmlPullParserException(
1444                "Unexpected end of document in <string>");
1445        } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1446            // all work already done by readThisPrimitiveValueXml
1447        } else if (tagName.equals("byte-array")) {
1448            res = readThisByteArrayXml(parser, "byte-array", name);
1449            name[0] = valueName;
1450            //System.out.println("Returning value for " + valueName + ": " + res);
1451            return res;
1452        } else if (tagName.equals("int-array")) {
1453            res = readThisIntArrayXml(parser, "int-array", name);
1454            name[0] = valueName;
1455            //System.out.println("Returning value for " + valueName + ": " + res);
1456            return res;
1457        } else if (tagName.equals("long-array")) {
1458            res = readThisLongArrayXml(parser, "long-array", name);
1459            name[0] = valueName;
1460            //System.out.println("Returning value for " + valueName + ": " + res);
1461            return res;
1462        } else if (tagName.equals("double-array")) {
1463            res = readThisDoubleArrayXml(parser, "double-array", name);
1464            name[0] = valueName;
1465            //System.out.println("Returning value for " + valueName + ": " + res);
1466            return res;
1467        } else if (tagName.equals("string-array")) {
1468            res = readThisStringArrayXml(parser, "string-array", name);
1469            name[0] = valueName;
1470            //System.out.println("Returning value for " + valueName + ": " + res);
1471            return res;
1472        } else if (tagName.equals("boolean-array")) {
1473            res = readThisBooleanArrayXml(parser, "boolean-array", name);
1474            name[0] = valueName;
1475            //System.out.println("Returning value for " + valueName + ": " + res);
1476            return res;
1477        } else if (tagName.equals("map")) {
1478            parser.next();
1479            res = arrayMap
1480                    ? readThisArrayMapXml(parser, "map", name, callback)
1481                    : readThisMapXml(parser, "map", name, callback);
1482            name[0] = valueName;
1483            //System.out.println("Returning value for " + valueName + ": " + res);
1484            return res;
1485        } else if (tagName.equals("list")) {
1486            parser.next();
1487            res = readThisListXml(parser, "list", name, callback, arrayMap);
1488            name[0] = valueName;
1489            //System.out.println("Returning value for " + valueName + ": " + res);
1490            return res;
1491        } else if (tagName.equals("set")) {
1492            parser.next();
1493            res = readThisSetXml(parser, "set", name, callback, arrayMap);
1494            name[0] = valueName;
1495            //System.out.println("Returning value for " + valueName + ": " + res);
1496            return res;
1497        } else if (callback != null) {
1498            res = callback.readThisUnknownObjectXml(parser, tagName);
1499            name[0] = valueName;
1500            return res;
1501        } else {
1502            throw new XmlPullParserException("Unknown tag: " + tagName);
1503        }
1504
1505        // Skip through to end tag.
1506        int eventType;
1507        while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1508            if (eventType == parser.END_TAG) {
1509                if (parser.getName().equals(tagName)) {
1510                    name[0] = valueName;
1511                    //System.out.println("Returning value for " + valueName + ": " + res);
1512                    return res;
1513                }
1514                throw new XmlPullParserException(
1515                    "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1516            } else if (eventType == parser.TEXT) {
1517                throw new XmlPullParserException(
1518                "Unexpected text in <" + tagName + ">: " + parser.getName());
1519            } else if (eventType == parser.START_TAG) {
1520                throw new XmlPullParserException(
1521                    "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1522            }
1523        }
1524        throw new XmlPullParserException(
1525            "Unexpected end of document in <" + tagName + ">");
1526    }
1527
1528    private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
1529    throws XmlPullParserException, java.io.IOException
1530    {
1531        try {
1532            if (tagName.equals("int")) {
1533                return Integer.parseInt(parser.getAttributeValue(null, "value"));
1534            } else if (tagName.equals("long")) {
1535                return Long.valueOf(parser.getAttributeValue(null, "value"));
1536            } else if (tagName.equals("float")) {
1537                return new Float(parser.getAttributeValue(null, "value"));
1538            } else if (tagName.equals("double")) {
1539                return new Double(parser.getAttributeValue(null, "value"));
1540            } else if (tagName.equals("boolean")) {
1541                return Boolean.valueOf(parser.getAttributeValue(null, "value"));
1542            } else {
1543                return null;
1544            }
1545        } catch (NullPointerException e) {
1546            throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
1547        } catch (NumberFormatException e) {
1548            throw new XmlPullParserException(
1549                    "Not a number in value attribute in <" + tagName + ">");
1550        }
1551    }
1552
1553    public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1554    {
1555        int type;
1556        while ((type=parser.next()) != parser.START_TAG
1557                   && type != parser.END_DOCUMENT) {
1558            ;
1559        }
1560
1561        if (type != parser.START_TAG) {
1562            throw new XmlPullParserException("No start tag found");
1563        }
1564
1565        if (!parser.getName().equals(firstElementName)) {
1566            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1567                    ", expected " + firstElementName);
1568        }
1569    }
1570
1571    public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1572    {
1573        int type;
1574        while ((type=parser.next()) != parser.START_TAG
1575                   && type != parser.END_DOCUMENT) {
1576            ;
1577        }
1578    }
1579
1580    public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1581            throws IOException, XmlPullParserException {
1582        for (;;) {
1583            int type = parser.next();
1584            if (type == XmlPullParser.END_DOCUMENT
1585                    || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1586                return false;
1587            }
1588            if (type == XmlPullParser.START_TAG
1589                    && parser.getDepth() == outerDepth + 1) {
1590                return true;
1591            }
1592        }
1593    }
1594
1595    public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1596        final String value = in.getAttributeValue(null, name);
1597        try {
1598            return Integer.parseInt(value);
1599        } catch (NumberFormatException e) {
1600            return defaultValue;
1601        }
1602    }
1603
1604    public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1605        final String value = in.getAttributeValue(null, name);
1606        try {
1607            return Integer.parseInt(value);
1608        } catch (NumberFormatException e) {
1609            throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1610        }
1611    }
1612
1613    public static void writeIntAttribute(XmlSerializer out, String name, int value)
1614            throws IOException {
1615        out.attribute(null, name, Integer.toString(value));
1616    }
1617
1618    public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1619        final String value = in.getAttributeValue(null, name);
1620        try {
1621            return Long.parseLong(value);
1622        } catch (NumberFormatException e) {
1623            return defaultValue;
1624        }
1625    }
1626
1627    public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1628        final String value = in.getAttributeValue(null, name);
1629        try {
1630            return Long.parseLong(value);
1631        } catch (NumberFormatException e) {
1632            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1633        }
1634    }
1635
1636    public static void writeLongAttribute(XmlSerializer out, String name, long value)
1637            throws IOException {
1638        out.attribute(null, name, Long.toString(value));
1639    }
1640
1641    public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
1642        final String value = in.getAttributeValue(null, name);
1643        try {
1644            return Float.parseFloat(value);
1645        } catch (NumberFormatException e) {
1646            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1647        }
1648    }
1649
1650    public static void writeFloatAttribute(XmlSerializer out, String name, float value)
1651            throws IOException {
1652        out.attribute(null, name, Float.toString(value));
1653    }
1654
1655    public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1656        final String value = in.getAttributeValue(null, name);
1657        return Boolean.parseBoolean(value);
1658    }
1659
1660    public static boolean readBooleanAttribute(XmlPullParser in, String name,
1661            boolean defaultValue) {
1662        final String value = in.getAttributeValue(null, name);
1663        if (value == null) {
1664            return defaultValue;
1665        } else {
1666            return Boolean.parseBoolean(value);
1667        }
1668    }
1669
1670    public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1671            throws IOException {
1672        out.attribute(null, name, Boolean.toString(value));
1673    }
1674
1675    public static Uri readUriAttribute(XmlPullParser in, String name) {
1676        final String value = in.getAttributeValue(null, name);
1677        return (value != null) ? Uri.parse(value) : null;
1678    }
1679
1680    public static void writeUriAttribute(XmlSerializer out, String name, Uri value)
1681            throws IOException {
1682        if (value != null) {
1683            out.attribute(null, name, value.toString());
1684        }
1685    }
1686
1687    public static String readStringAttribute(XmlPullParser in, String name) {
1688        return in.getAttributeValue(null, name);
1689    }
1690
1691    public static void writeStringAttribute(XmlSerializer out, String name, String value)
1692            throws IOException {
1693        if (value != null) {
1694            out.attribute(null, name, value);
1695        }
1696    }
1697
1698    public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
1699        final String value = in.getAttributeValue(null, name);
1700        if (value != null) {
1701            return Base64.decode(value, Base64.DEFAULT);
1702        } else {
1703            return null;
1704        }
1705    }
1706
1707    public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
1708            throws IOException {
1709        if (value != null) {
1710            out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT));
1711        }
1712    }
1713
1714    public static Bitmap readBitmapAttribute(XmlPullParser in, String name) {
1715        final byte[] value = readByteArrayAttribute(in, name);
1716        if (value != null) {
1717            return BitmapFactory.decodeByteArray(value, 0, value.length);
1718        } else {
1719            return null;
1720        }
1721    }
1722
1723    @Deprecated
1724    public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
1725            throws IOException {
1726        if (value != null) {
1727            final ByteArrayOutputStream os = new ByteArrayOutputStream();
1728            value.compress(CompressFormat.PNG, 90, os);
1729            writeByteArrayAttribute(out, name, os.toByteArray());
1730        }
1731    }
1732
1733    /** @hide */
1734    public interface WriteMapCallback {
1735        /**
1736         * Called from writeMapXml when an Object type is not recognized. The implementer
1737         * must write out the entire element including start and end tags.
1738         *
1739         * @param v The object to be written out
1740         * @param name The mapping key for v. Must be written into the "name" attribute of the
1741         *             start tag.
1742         * @param out The XML output stream.
1743         * @throws XmlPullParserException on unrecognized Object type.
1744         * @throws IOException on XmlSerializer serialization errors.
1745         * @hide
1746         */
1747         public void writeUnknownObject(Object v, String name, XmlSerializer out)
1748                 throws XmlPullParserException, IOException;
1749    }
1750
1751    /** @hide */
1752    public interface ReadMapCallback {
1753        /**
1754         * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1755         * is positioned within the start tag so that attributes can be read using in.getAttribute.
1756         *
1757         * @param in the XML input stream
1758         * @param tag the START_TAG that was not recognized.
1759         * @return the Object parsed from the stream which will be put into the map.
1760         * @throws XmlPullParserException if the START_TAG is not recognized.
1761         * @throws IOException on XmlPullParser serialization errors.
1762         * @hide
1763         */
1764        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
1765                throws XmlPullParserException, IOException;
1766    }
1767}
1768