XmlUtils.java revision 2ddce3226e2a1f380dfc4ad3f7e962dfdbfc39dd
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
19
20import org.xmlpull.v1.XmlPullParser;
21import org.xmlpull.v1.XmlPullParserException;
22import org.xmlpull.v1.XmlSerializer;
23
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.OutputStream;
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.Iterator;
31import java.util.List;
32import java.util.Map;
33import java.util.Set;
34
35import android.util.Xml;
36
37/** {@hide} */
38public class XmlUtils
39{
40
41    public static void skipCurrentTag(XmlPullParser parser)
42            throws XmlPullParserException, IOException {
43        int outerDepth = parser.getDepth();
44        int type;
45        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
46               && (type != XmlPullParser.END_TAG
47                       || parser.getDepth() > outerDepth)) {
48        }
49    }
50
51    public static final int
52    convertValueToList(CharSequence value, String[] options, int defaultValue)
53    {
54        if (null != value) {
55            for (int i = 0; i < options.length; i++) {
56                if (value.equals(options[i]))
57                    return i;
58            }
59        }
60
61        return defaultValue;
62    }
63
64    public static final boolean
65    convertValueToBoolean(CharSequence value, boolean defaultValue)
66    {
67        boolean result = false;
68
69        if (null == value)
70            return defaultValue;
71
72        if (value.equals("1")
73        ||  value.equals("true")
74        ||  value.equals("TRUE"))
75            result = true;
76
77        return result;
78    }
79
80    public static final int
81    convertValueToInt(CharSequence charSeq, int defaultValue)
82    {
83        if (null == charSeq)
84            return defaultValue;
85
86        String nm = charSeq.toString();
87
88        // XXX This code is copied from Integer.decode() so we don't
89        // have to instantiate an Integer!
90
91        int value;
92        int sign = 1;
93        int index = 0;
94        int len = nm.length();
95        int base = 10;
96
97        if ('-' == nm.charAt(0)) {
98            sign = -1;
99            index++;
100        }
101
102        if ('0' == nm.charAt(index)) {
103            //  Quick check for a zero by itself
104            if (index == (len - 1))
105                return 0;
106
107            char    c = nm.charAt(index + 1);
108
109            if ('x' == c || 'X' == c) {
110                index += 2;
111                base = 16;
112            } else {
113                index++;
114                base = 8;
115            }
116        }
117        else if ('#' == nm.charAt(index))
118        {
119            index++;
120            base = 16;
121        }
122
123        return Integer.parseInt(nm.substring(index), base) * sign;
124    }
125
126    public static final int
127    convertValueToUnsignedInt(String value, int defaultValue)
128    {
129        if (null == value)
130            return defaultValue;
131
132        return parseUnsignedIntAttribute(value);
133    }
134
135    public static final int
136    parseUnsignedIntAttribute(CharSequence charSeq)
137    {
138        String  value = charSeq.toString();
139
140        long    bits;
141        int     index = 0;
142        int     len = value.length();
143        int     base = 10;
144
145        if ('0' == value.charAt(index)) {
146            //  Quick check for zero by itself
147            if (index == (len - 1))
148                return 0;
149
150            char    c = value.charAt(index + 1);
151
152            if ('x' == c || 'X' == c) {     //  check for hex
153                index += 2;
154                base = 16;
155            } else {                        //  check for octal
156                index++;
157                base = 8;
158            }
159        } else if ('#' == value.charAt(index)) {
160            index++;
161            base = 16;
162        }
163
164        return (int) Long.parseLong(value.substring(index), base);
165    }
166
167    /**
168     * Flatten a Map into an output stream as XML.  The map can later be
169     * read back with readMapXml().
170     *
171     * @param val The map to be flattened.
172     * @param out Where to write the XML data.
173     *
174     * @see #writeMapXml(Map, String, XmlSerializer)
175     * @see #writeListXml
176     * @see #writeValueXml
177     * @see #readMapXml
178     */
179    public static final void writeMapXml(Map val, OutputStream out)
180            throws XmlPullParserException, java.io.IOException {
181        XmlSerializer serializer = new FastXmlSerializer();
182        serializer.setOutput(out, "utf-8");
183        serializer.startDocument(null, true);
184        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
185        writeMapXml(val, null, serializer);
186        serializer.endDocument();
187    }
188
189    /**
190     * Flatten a List into an output stream as XML.  The list can later be
191     * read back with readListXml().
192     *
193     * @param val The list to be flattened.
194     * @param out Where to write the XML data.
195     *
196     * @see #writeListXml(List, String, XmlSerializer)
197     * @see #writeMapXml
198     * @see #writeValueXml
199     * @see #readListXml
200     */
201    public static final void writeListXml(List val, OutputStream out)
202    throws XmlPullParserException, java.io.IOException
203    {
204        XmlSerializer serializer = Xml.newSerializer();
205        serializer.setOutput(out, "utf-8");
206        serializer.startDocument(null, true);
207        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
208        writeListXml(val, null, serializer);
209        serializer.endDocument();
210    }
211
212    /**
213     * Flatten a Map into an XmlSerializer.  The map can later be read back
214     * with readThisMapXml().
215     *
216     * @param val The map to be flattened.
217     * @param name Name attribute to include with this list's tag, or null for
218     *             none.
219     * @param out XmlSerializer to write the map into.
220     *
221     * @see #writeMapXml(Map, OutputStream)
222     * @see #writeListXml
223     * @see #writeValueXml
224     * @see #readMapXml
225     */
226    public static final void writeMapXml(Map val, String name, XmlSerializer out)
227    throws XmlPullParserException, java.io.IOException
228    {
229        if (val == null) {
230            out.startTag(null, "null");
231            out.endTag(null, "null");
232            return;
233        }
234
235        Set s = val.entrySet();
236        Iterator i = s.iterator();
237
238        out.startTag(null, "map");
239        if (name != null) {
240            out.attribute(null, "name", name);
241        }
242
243        while (i.hasNext()) {
244            Map.Entry e = (Map.Entry)i.next();
245            writeValueXml(e.getValue(), (String)e.getKey(), out);
246        }
247
248        out.endTag(null, "map");
249    }
250
251    /**
252     * Flatten a List into an XmlSerializer.  The list can later be read back
253     * with readThisListXml().
254     *
255     * @param val The list to be flattened.
256     * @param name Name attribute to include with this list's tag, or null for
257     *             none.
258     * @param out XmlSerializer to write the list into.
259     *
260     * @see #writeListXml(List, OutputStream)
261     * @see #writeMapXml
262     * @see #writeValueXml
263     * @see #readListXml
264     */
265    public static final void writeListXml(List val, String name, XmlSerializer out)
266    throws XmlPullParserException, java.io.IOException
267    {
268        if (val == null) {
269            out.startTag(null, "null");
270            out.endTag(null, "null");
271            return;
272        }
273
274        out.startTag(null, "list");
275        if (name != null) {
276            out.attribute(null, "name", name);
277        }
278
279        int N = val.size();
280        int i=0;
281        while (i < N) {
282            writeValueXml(val.get(i), null, out);
283            i++;
284        }
285
286        out.endTag(null, "list");
287    }
288
289    public static final void writeSetXml(Set val, String name, XmlSerializer out)
290            throws XmlPullParserException, java.io.IOException {
291        if (val == null) {
292            out.startTag(null, "null");
293            out.endTag(null, "null");
294            return;
295        }
296
297        out.startTag(null, "set");
298        if (name != null) {
299            out.attribute(null, "name", name);
300        }
301
302        for (Object v : val) {
303            writeValueXml(v, null, out);
304        }
305
306        out.endTag(null, "set");
307    }
308
309    /**
310     * Flatten a byte[] into an XmlSerializer.  The list can later be read back
311     * with readThisByteArrayXml().
312     *
313     * @param val The byte array to be flattened.
314     * @param name Name attribute to include with this array's tag, or null for
315     *             none.
316     * @param out XmlSerializer to write the array into.
317     *
318     * @see #writeMapXml
319     * @see #writeValueXml
320     */
321    public static final void writeByteArrayXml(byte[] val, String name,
322            XmlSerializer out)
323            throws XmlPullParserException, java.io.IOException {
324
325        if (val == null) {
326            out.startTag(null, "null");
327            out.endTag(null, "null");
328            return;
329        }
330
331        out.startTag(null, "byte-array");
332        if (name != null) {
333            out.attribute(null, "name", name);
334        }
335
336        final int N = val.length;
337        out.attribute(null, "num", Integer.toString(N));
338
339        StringBuilder sb = new StringBuilder(val.length*2);
340        for (int i=0; i<N; i++) {
341            int b = val[i];
342            int h = b>>4;
343            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
344            h = b&0xff;
345            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
346        }
347
348        out.text(sb.toString());
349
350        out.endTag(null, "byte-array");
351    }
352
353    /**
354     * Flatten an int[] into an XmlSerializer.  The list can later be read back
355     * with readThisIntArrayXml().
356     *
357     * @param val The int array to be flattened.
358     * @param name Name attribute to include with this array's tag, or null for
359     *             none.
360     * @param out XmlSerializer to write the array into.
361     *
362     * @see #writeMapXml
363     * @see #writeValueXml
364     * @see #readThisIntArrayXml
365     */
366    public static final void writeIntArrayXml(int[] val, String name,
367            XmlSerializer out)
368            throws XmlPullParserException, java.io.IOException {
369
370        if (val == null) {
371            out.startTag(null, "null");
372            out.endTag(null, "null");
373            return;
374        }
375
376        out.startTag(null, "int-array");
377        if (name != null) {
378            out.attribute(null, "name", name);
379        }
380
381        final int N = val.length;
382        out.attribute(null, "num", Integer.toString(N));
383
384        for (int i=0; i<N; i++) {
385            out.startTag(null, "item");
386            out.attribute(null, "value", Integer.toString(val[i]));
387            out.endTag(null, "item");
388        }
389
390        out.endTag(null, "int-array");
391    }
392
393    /**
394     * Flatten an object's value into an XmlSerializer.  The value can later
395     * be read back with readThisValueXml().
396     *
397     * Currently supported value types are: null, String, Integer, Long,
398     * Float, Double Boolean, Map, List.
399     *
400     * @param v The object to be flattened.
401     * @param name Name attribute to include with this value's tag, or null
402     *             for none.
403     * @param out XmlSerializer to write the object into.
404     *
405     * @see #writeMapXml
406     * @see #writeListXml
407     * @see #readValueXml
408     */
409    public static final void writeValueXml(Object v, String name, XmlSerializer out)
410    throws XmlPullParserException, java.io.IOException
411    {
412        String typeStr;
413        if (v == null) {
414            out.startTag(null, "null");
415            if (name != null) {
416                out.attribute(null, "name", name);
417            }
418            out.endTag(null, "null");
419            return;
420        } else if (v instanceof String) {
421            out.startTag(null, "string");
422            if (name != null) {
423                out.attribute(null, "name", name);
424            }
425            out.text(v.toString());
426            out.endTag(null, "string");
427            return;
428        } else if (v instanceof Integer) {
429            typeStr = "int";
430        } else if (v instanceof Long) {
431            typeStr = "long";
432        } else if (v instanceof Float) {
433            typeStr = "float";
434        } else if (v instanceof Double) {
435            typeStr = "double";
436        } else if (v instanceof Boolean) {
437            typeStr = "boolean";
438        } else if (v instanceof byte[]) {
439            writeByteArrayXml((byte[])v, name, out);
440            return;
441        } else if (v instanceof int[]) {
442            writeIntArrayXml((int[])v, name, out);
443            return;
444        } else if (v instanceof Map) {
445            writeMapXml((Map)v, name, out);
446            return;
447        } else if (v instanceof List) {
448            writeListXml((List)v, name, out);
449            return;
450        } else if (v instanceof Set) {
451            writeSetXml((Set)v, name, out);
452            return;
453        } else if (v instanceof CharSequence) {
454            // XXX This is to allow us to at least write something if
455            // we encounter styled text...  but it means we will drop all
456            // of the styling information. :(
457            out.startTag(null, "string");
458            if (name != null) {
459                out.attribute(null, "name", name);
460            }
461            out.text(v.toString());
462            out.endTag(null, "string");
463            return;
464        } else {
465            throw new RuntimeException("writeValueXml: unable to write value " + v);
466        }
467
468        out.startTag(null, typeStr);
469        if (name != null) {
470            out.attribute(null, "name", name);
471        }
472        out.attribute(null, "value", v.toString());
473        out.endTag(null, typeStr);
474    }
475
476    /**
477     * Read a HashMap from an InputStream containing XML.  The stream can
478     * previously have been written by writeMapXml().
479     *
480     * @param in The InputStream from which to read.
481     *
482     * @return HashMap The resulting map.
483     *
484     * @see #readListXml
485     * @see #readValueXml
486     * @see #readThisMapXml
487     * #see #writeMapXml
488     */
489    public static final HashMap readMapXml(InputStream in)
490    throws XmlPullParserException, java.io.IOException
491    {
492        XmlPullParser   parser = Xml.newPullParser();
493        parser.setInput(in, null);
494        return (HashMap)readValueXml(parser, new String[1]);
495    }
496
497    /**
498     * Read an ArrayList from an InputStream containing XML.  The stream can
499     * previously have been written by writeListXml().
500     *
501     * @param in The InputStream from which to read.
502     *
503     * @return ArrayList The resulting list.
504     *
505     * @see #readMapXml
506     * @see #readValueXml
507     * @see #readThisListXml
508     * @see #writeListXml
509     */
510    public static final ArrayList readListXml(InputStream in)
511    throws XmlPullParserException, java.io.IOException
512    {
513        XmlPullParser   parser = Xml.newPullParser();
514        parser.setInput(in, null);
515        return (ArrayList)readValueXml(parser, new String[1]);
516    }
517
518
519    /**
520     * Read a HashSet from an InputStream containing XML. The stream can
521     * previously have been written by writeSetXml().
522     *
523     * @param in The InputStream from which to read.
524     *
525     * @return HashSet The resulting set.
526     *
527     * @throws XmlPullParserException
528     * @throws java.io.IOException
529     *
530     * @see #readValueXml
531     * @see #readThisSetXml
532     * @see #writeSetXml
533     */
534    public static final HashSet readSetXml(InputStream in)
535            throws XmlPullParserException, java.io.IOException {
536        XmlPullParser parser = Xml.newPullParser();
537        parser.setInput(in, null);
538        return (HashSet) readValueXml(parser, new String[1]);
539    }
540
541    /**
542     * Read a HashMap object from an XmlPullParser.  The XML data could
543     * previously have been generated by writeMapXml().  The XmlPullParser
544     * must be positioned <em>after</em> the tag that begins the map.
545     *
546     * @param parser The XmlPullParser from which to read the map data.
547     * @param endTag Name of the tag that will end the map, usually "map".
548     * @param name An array of one string, used to return the name attribute
549     *             of the map's tag.
550     *
551     * @return HashMap The newly generated map.
552     *
553     * @see #readMapXml
554     */
555    public static final HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name)
556    throws XmlPullParserException, java.io.IOException
557    {
558        HashMap map = new HashMap();
559
560        int eventType = parser.getEventType();
561        do {
562            if (eventType == parser.START_TAG) {
563                Object val = readThisValueXml(parser, name);
564                if (name[0] != null) {
565                    //System.out.println("Adding to map: " + name + " -> " + val);
566                    map.put(name[0], val);
567                } else {
568                    throw new XmlPullParserException(
569                        "Map value without name attribute: " + parser.getName());
570                }
571            } else if (eventType == parser.END_TAG) {
572                if (parser.getName().equals(endTag)) {
573                    return map;
574                }
575                throw new XmlPullParserException(
576                    "Expected " + endTag + " end tag at: " + parser.getName());
577            }
578            eventType = parser.next();
579        } while (eventType != parser.END_DOCUMENT);
580
581        throw new XmlPullParserException(
582            "Document ended before " + endTag + " end tag");
583    }
584
585    /**
586     * Read an ArrayList object from an XmlPullParser.  The XML data could
587     * previously have been generated by writeListXml().  The XmlPullParser
588     * must be positioned <em>after</em> the tag that begins the list.
589     *
590     * @param parser The XmlPullParser from which to read the list data.
591     * @param endTag Name of the tag that will end the list, usually "list".
592     * @param name An array of one string, used to return the name attribute
593     *             of the list's tag.
594     *
595     * @return HashMap The newly generated list.
596     *
597     * @see #readListXml
598     */
599    public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name)
600    throws XmlPullParserException, java.io.IOException
601    {
602        ArrayList list = new ArrayList();
603
604        int eventType = parser.getEventType();
605        do {
606            if (eventType == parser.START_TAG) {
607                Object val = readThisValueXml(parser, name);
608                list.add(val);
609                //System.out.println("Adding to list: " + val);
610            } else if (eventType == parser.END_TAG) {
611                if (parser.getName().equals(endTag)) {
612                    return list;
613                }
614                throw new XmlPullParserException(
615                    "Expected " + endTag + " end tag at: " + parser.getName());
616            }
617            eventType = parser.next();
618        } while (eventType != parser.END_DOCUMENT);
619
620        throw new XmlPullParserException(
621            "Document ended before " + endTag + " end tag");
622    }
623
624    /**
625     * Read a HashSet object from an XmlPullParser. The XML data could previously
626     * have been generated by writeSetXml(). The XmlPullParser must be positioned
627     * <em>after</em> the tag that begins the set.
628     *
629     * @param parser The XmlPullParser from which to read the set data.
630     * @param endTag Name of the tag that will end the set, usually "set".
631     * @param name An array of one string, used to return the name attribute
632     *             of the set's tag.
633     *
634     * @return HashSet The newly generated set.
635     *
636     * @throws XmlPullParserException
637     * @throws java.io.IOException
638     *
639     * @see #readSetXml
640     */
641    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
642            throws XmlPullParserException, java.io.IOException {
643        HashSet set = new HashSet();
644
645        int eventType = parser.getEventType();
646        do {
647            if (eventType == parser.START_TAG) {
648                Object val = readThisValueXml(parser, name);
649                set.add(val);
650                //System.out.println("Adding to set: " + val);
651            } else if (eventType == parser.END_TAG) {
652                if (parser.getName().equals(endTag)) {
653                    return set;
654                }
655                throw new XmlPullParserException(
656                        "Expected " + endTag + " end tag at: " + parser.getName());
657            }
658            eventType = parser.next();
659        } while (eventType != parser.END_DOCUMENT);
660
661        throw new XmlPullParserException(
662                "Document ended before " + endTag + " end tag");
663    }
664
665    /**
666     * Read an int[] object from an XmlPullParser.  The XML data could
667     * previously have been generated by writeIntArrayXml().  The XmlPullParser
668     * must be positioned <em>after</em> the tag that begins the list.
669     *
670     * @param parser The XmlPullParser from which to read the list data.
671     * @param endTag Name of the tag that will end the list, usually "list".
672     * @param name An array of one string, used to return the name attribute
673     *             of the list's tag.
674     *
675     * @return Returns a newly generated int[].
676     *
677     * @see #readListXml
678     */
679    public static final int[] readThisIntArrayXml(XmlPullParser parser,
680            String endTag, String[] name)
681            throws XmlPullParserException, java.io.IOException {
682
683        int num;
684        try {
685            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
686        } catch (NullPointerException e) {
687            throw new XmlPullParserException(
688                    "Need num attribute in byte-array");
689        } catch (NumberFormatException e) {
690            throw new XmlPullParserException(
691                    "Not a number in num attribute in byte-array");
692        }
693
694        int[] array = new int[num];
695        int i = 0;
696
697        int eventType = parser.getEventType();
698        do {
699            if (eventType == parser.START_TAG) {
700                if (parser.getName().equals("item")) {
701                    try {
702                        array[i] = Integer.parseInt(
703                                parser.getAttributeValue(null, "value"));
704                    } catch (NullPointerException e) {
705                        throw new XmlPullParserException(
706                                "Need value attribute in item");
707                    } catch (NumberFormatException e) {
708                        throw new XmlPullParserException(
709                                "Not a number in value attribute in item");
710                    }
711                } else {
712                    throw new XmlPullParserException(
713                            "Expected item tag at: " + parser.getName());
714                }
715            } else if (eventType == parser.END_TAG) {
716                if (parser.getName().equals(endTag)) {
717                    return array;
718                } else if (parser.getName().equals("item")) {
719                    i++;
720                } else {
721                    throw new XmlPullParserException(
722                        "Expected " + endTag + " end tag at: "
723                        + parser.getName());
724                }
725            }
726            eventType = parser.next();
727        } while (eventType != parser.END_DOCUMENT);
728
729        throw new XmlPullParserException(
730            "Document ended before " + endTag + " end tag");
731    }
732
733    /**
734     * Read a flattened object from an XmlPullParser.  The XML data could
735     * previously have been written with writeMapXml(), writeListXml(), or
736     * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
737     * tag that defines the value.
738     *
739     * @param parser The XmlPullParser from which to read the object.
740     * @param name An array of one string, used to return the name attribute
741     *             of the value's tag.
742     *
743     * @return Object The newly generated value object.
744     *
745     * @see #readMapXml
746     * @see #readListXml
747     * @see #writeValueXml
748     */
749    public static final Object readValueXml(XmlPullParser parser, String[] name)
750    throws XmlPullParserException, java.io.IOException
751    {
752        int eventType = parser.getEventType();
753        do {
754            if (eventType == parser.START_TAG) {
755                return readThisValueXml(parser, name);
756            } else if (eventType == parser.END_TAG) {
757                throw new XmlPullParserException(
758                    "Unexpected end tag at: " + parser.getName());
759            } else if (eventType == parser.TEXT) {
760                throw new XmlPullParserException(
761                    "Unexpected text: " + parser.getText());
762            }
763            eventType = parser.next();
764        } while (eventType != parser.END_DOCUMENT);
765
766        throw new XmlPullParserException(
767            "Unexpected end of document");
768    }
769
770    private static final Object readThisValueXml(XmlPullParser parser, String[] name)
771    throws XmlPullParserException, java.io.IOException
772    {
773        final String valueName = parser.getAttributeValue(null, "name");
774        final String tagName = parser.getName();
775
776        //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
777
778        Object res;
779
780        if (tagName.equals("null")) {
781            res = null;
782        } else if (tagName.equals("string")) {
783            String value = "";
784            int eventType;
785            while ((eventType = parser.next()) != parser.END_DOCUMENT) {
786                if (eventType == parser.END_TAG) {
787                    if (parser.getName().equals("string")) {
788                        name[0] = valueName;
789                        //System.out.println("Returning value for " + valueName + ": " + value);
790                        return value;
791                    }
792                    throw new XmlPullParserException(
793                        "Unexpected end tag in <string>: " + parser.getName());
794                } else if (eventType == parser.TEXT) {
795                    value += parser.getText();
796                } else if (eventType == parser.START_TAG) {
797                    throw new XmlPullParserException(
798                        "Unexpected start tag in <string>: " + parser.getName());
799                }
800            }
801            throw new XmlPullParserException(
802                "Unexpected end of document in <string>");
803        } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
804            // all work already done by readThisPrimitiveValueXml
805        } else if (tagName.equals("int-array")) {
806            parser.next();
807            res = readThisIntArrayXml(parser, "int-array", name);
808            name[0] = valueName;
809            //System.out.println("Returning value for " + valueName + ": " + res);
810            return res;
811        } else if (tagName.equals("map")) {
812            parser.next();
813            res = readThisMapXml(parser, "map", name);
814            name[0] = valueName;
815            //System.out.println("Returning value for " + valueName + ": " + res);
816            return res;
817        } else if (tagName.equals("list")) {
818            parser.next();
819            res = readThisListXml(parser, "list", name);
820            name[0] = valueName;
821            //System.out.println("Returning value for " + valueName + ": " + res);
822            return res;
823        } else if (tagName.equals("set")) {
824            parser.next();
825            res = readThisSetXml(parser, "set", name);
826            name[0] = valueName;
827            //System.out.println("Returning value for " + valueName + ": " + res);
828            return res;
829        } else {
830            throw new XmlPullParserException(
831                "Unknown tag: " + tagName);
832        }
833
834        // Skip through to end tag.
835        int eventType;
836        while ((eventType = parser.next()) != parser.END_DOCUMENT) {
837            if (eventType == parser.END_TAG) {
838                if (parser.getName().equals(tagName)) {
839                    name[0] = valueName;
840                    //System.out.println("Returning value for " + valueName + ": " + res);
841                    return res;
842                }
843                throw new XmlPullParserException(
844                    "Unexpected end tag in <" + tagName + ">: " + parser.getName());
845            } else if (eventType == parser.TEXT) {
846                throw new XmlPullParserException(
847                "Unexpected text in <" + tagName + ">: " + parser.getName());
848            } else if (eventType == parser.START_TAG) {
849                throw new XmlPullParserException(
850                    "Unexpected start tag in <" + tagName + ">: " + parser.getName());
851            }
852        }
853        throw new XmlPullParserException(
854            "Unexpected end of document in <" + tagName + ">");
855    }
856
857    private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
858    throws XmlPullParserException, java.io.IOException
859    {
860        try {
861            if (tagName.equals("int")) {
862                return Integer.parseInt(parser.getAttributeValue(null, "value"));
863            } else if (tagName.equals("long")) {
864                return Long.valueOf(parser.getAttributeValue(null, "value"));
865            } else if (tagName.equals("float")) {
866                return new Float(parser.getAttributeValue(null, "value"));
867            } else if (tagName.equals("double")) {
868                return new Double(parser.getAttributeValue(null, "value"));
869            } else if (tagName.equals("boolean")) {
870                return Boolean.valueOf(parser.getAttributeValue(null, "value"));
871            } else {
872                return null;
873            }
874        } catch (NullPointerException e) {
875            throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
876        } catch (NumberFormatException e) {
877            throw new XmlPullParserException(
878                    "Not a number in value attribute in <" + tagName + ">");
879        }
880    }
881
882    public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
883    {
884        int type;
885        while ((type=parser.next()) != parser.START_TAG
886                   && type != parser.END_DOCUMENT) {
887            ;
888        }
889
890        if (type != parser.START_TAG) {
891            throw new XmlPullParserException("No start tag found");
892        }
893
894        if (!parser.getName().equals(firstElementName)) {
895            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
896                    ", expected " + firstElementName);
897        }
898    }
899
900    public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
901    {
902        int type;
903        while ((type=parser.next()) != parser.START_TAG
904                   && type != parser.END_DOCUMENT) {
905            ;
906        }
907    }
908
909    public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
910            throws IOException, XmlPullParserException {
911        for (;;) {
912            int type = parser.next();
913            if (type == XmlPullParser.END_DOCUMENT
914                    || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
915                return false;
916            }
917            if (type == XmlPullParser.START_TAG
918                    && parser.getDepth() == outerDepth + 1) {
919                return true;
920            }
921        }
922    }
923}
924