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