XmlUtils.java revision ef73ee1dd98acfc4a19561367cfc3e4d8bbe06ea
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        writeMapXml(val, name, out, null);
225    }
226
227    /**
228     * Flatten a Map into an XmlSerializer.  The map can later be read back
229     * with readThisMapXml().
230     *
231     * @param val The map to be flattened.
232     * @param name Name attribute to include with this list's tag, or null for
233     *             none.
234     * @param out XmlSerializer to write the map into.
235     * @param callback Method to call when an Object type is not recognized.
236     *
237     * @see #writeMapXml(Map, OutputStream)
238     * @see #writeListXml
239     * @see #writeValueXml
240     * @see #readMapXml
241     *
242     * @hide
243     */
244    public static final void writeMapXml(Map val, String name, XmlSerializer out,
245            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
246
247        if (val == null) {
248            out.startTag(null, "null");
249            out.endTag(null, "null");
250            return;
251        }
252
253        out.startTag(null, "map");
254        if (name != null) {
255            out.attribute(null, "name", name);
256        }
257
258        writeMapXml(val, out, callback);
259
260        out.endTag(null, "map");
261    }
262
263    /**
264     * Flatten a Map into an XmlSerializer.  The map can later be read back
265     * with readThisMapXml(). This method presumes that the start tag and
266     * name attribute have already been written and does not write an end tag.
267     *
268     * @param val The map to be flattened.
269     * @param out XmlSerializer to write the map into.
270     *
271     * @see #writeMapXml(Map, OutputStream)
272     * @see #writeListXml
273     * @see #writeValueXml
274     * @see #readMapXml
275     *
276     * @hide
277     */
278    public static final void writeMapXml(Map val, XmlSerializer out,
279            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
280        if (val == null) {
281            return;
282        }
283
284        Set s = val.entrySet();
285        Iterator i = s.iterator();
286
287        while (i.hasNext()) {
288            Map.Entry e = (Map.Entry)i.next();
289            writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
290        }
291    }
292
293    /**
294     * Flatten a List into an XmlSerializer.  The list can later be read back
295     * with readThisListXml().
296     *
297     * @param val The list to be flattened.
298     * @param name Name attribute to include with this list's tag, or null for
299     *             none.
300     * @param out XmlSerializer to write the list into.
301     *
302     * @see #writeListXml(List, OutputStream)
303     * @see #writeMapXml
304     * @see #writeValueXml
305     * @see #readListXml
306     */
307    public static final void writeListXml(List val, String name, XmlSerializer out)
308    throws XmlPullParserException, java.io.IOException
309    {
310        if (val == null) {
311            out.startTag(null, "null");
312            out.endTag(null, "null");
313            return;
314        }
315
316        out.startTag(null, "list");
317        if (name != null) {
318            out.attribute(null, "name", name);
319        }
320
321        int N = val.size();
322        int i=0;
323        while (i < N) {
324            writeValueXml(val.get(i), null, out);
325            i++;
326        }
327
328        out.endTag(null, "list");
329    }
330
331    public static final void writeSetXml(Set val, String name, XmlSerializer out)
332            throws XmlPullParserException, java.io.IOException {
333        if (val == null) {
334            out.startTag(null, "null");
335            out.endTag(null, "null");
336            return;
337        }
338
339        out.startTag(null, "set");
340        if (name != null) {
341            out.attribute(null, "name", name);
342        }
343
344        for (Object v : val) {
345            writeValueXml(v, null, out);
346        }
347
348        out.endTag(null, "set");
349    }
350
351    /**
352     * Flatten a byte[] into an XmlSerializer.  The list can later be read back
353     * with readThisByteArrayXml().
354     *
355     * @param val The byte array to be flattened.
356     * @param name Name attribute to include with this array's tag, or null for
357     *             none.
358     * @param out XmlSerializer to write the array into.
359     *
360     * @see #writeMapXml
361     * @see #writeValueXml
362     */
363    public static final void writeByteArrayXml(byte[] val, String name,
364            XmlSerializer out)
365            throws XmlPullParserException, java.io.IOException {
366
367        if (val == null) {
368            out.startTag(null, "null");
369            out.endTag(null, "null");
370            return;
371        }
372
373        out.startTag(null, "byte-array");
374        if (name != null) {
375            out.attribute(null, "name", name);
376        }
377
378        final int N = val.length;
379        out.attribute(null, "num", Integer.toString(N));
380
381        StringBuilder sb = new StringBuilder(val.length*2);
382        for (int i=0; i<N; i++) {
383            int b = val[i];
384            int h = b>>4;
385            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
386            h = b&0xff;
387            sb.append(h >= 10 ? ('a'+h-10) : ('0'+h));
388        }
389
390        out.text(sb.toString());
391
392        out.endTag(null, "byte-array");
393    }
394
395    /**
396     * Flatten an int[] into an XmlSerializer.  The list can later be read back
397     * with readThisIntArrayXml().
398     *
399     * @param val The int array to be flattened.
400     * @param name Name attribute to include with this array's tag, or null for
401     *             none.
402     * @param out XmlSerializer to write the array into.
403     *
404     * @see #writeMapXml
405     * @see #writeValueXml
406     * @see #readThisIntArrayXml
407     */
408    public static final void writeIntArrayXml(int[] val, String name,
409            XmlSerializer out)
410            throws XmlPullParserException, java.io.IOException {
411
412        if (val == null) {
413            out.startTag(null, "null");
414            out.endTag(null, "null");
415            return;
416        }
417
418        out.startTag(null, "int-array");
419        if (name != null) {
420            out.attribute(null, "name", name);
421        }
422
423        final int N = val.length;
424        out.attribute(null, "num", Integer.toString(N));
425
426        for (int i=0; i<N; i++) {
427            out.startTag(null, "item");
428            out.attribute(null, "value", Integer.toString(val[i]));
429            out.endTag(null, "item");
430        }
431
432        out.endTag(null, "int-array");
433    }
434
435    /**
436     * Flatten a long[] into an XmlSerializer.  The list can later be read back
437     * with readThisLongArrayXml().
438     *
439     * @param val The long array to be flattened.
440     * @param name Name attribute to include with this array's tag, or null for
441     *             none.
442     * @param out XmlSerializer to write the array into.
443     *
444     * @see #writeMapXml
445     * @see #writeValueXml
446     * @see #readThisIntArrayXml
447     */
448    public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
449            throws XmlPullParserException, java.io.IOException {
450
451        if (val == null) {
452            out.startTag(null, "null");
453            out.endTag(null, "null");
454            return;
455        }
456
457        out.startTag(null, "long-array");
458        if (name != null) {
459            out.attribute(null, "name", name);
460        }
461
462        final int N = val.length;
463        out.attribute(null, "num", Integer.toString(N));
464
465        for (int i=0; i<N; i++) {
466            out.startTag(null, "item");
467            out.attribute(null, "value", Long.toString(val[i]));
468            out.endTag(null, "item");
469        }
470
471        out.endTag(null, "long-array");
472    }
473
474    /**
475     * Flatten a double[] into an XmlSerializer.  The list can later be read back
476     * with readThisDoubleArrayXml().
477     *
478     * @param val The double array to be flattened.
479     * @param name Name attribute to include with this array's tag, or null for
480     *             none.
481     * @param out XmlSerializer to write the array into.
482     *
483     * @see #writeMapXml
484     * @see #writeValueXml
485     * @see #readThisIntArrayXml
486     */
487    public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
488            throws XmlPullParserException, java.io.IOException {
489
490        if (val == null) {
491            out.startTag(null, "null");
492            out.endTag(null, "null");
493            return;
494        }
495
496        out.startTag(null, "double-array");
497        if (name != null) {
498            out.attribute(null, "name", name);
499        }
500
501        final int N = val.length;
502        out.attribute(null, "num", Integer.toString(N));
503
504        for (int i=0; i<N; i++) {
505            out.startTag(null, "item");
506            out.attribute(null, "value", Double.toString(val[i]));
507            out.endTag(null, "item");
508        }
509
510        out.endTag(null, "double-array");
511    }
512
513    /**
514     * Flatten a String[] into an XmlSerializer.  The list can later be read back
515     * with readThisStringArrayXml().
516     *
517     * @param val The long array to be flattened.
518     * @param name Name attribute to include with this array's tag, or null for
519     *             none.
520     * @param out XmlSerializer to write the array into.
521     *
522     * @see #writeMapXml
523     * @see #writeValueXml
524     * @see #readThisIntArrayXml
525     */
526    public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
527            throws XmlPullParserException, java.io.IOException {
528
529        if (val == null) {
530            out.startTag(null, "null");
531            out.endTag(null, "null");
532            return;
533        }
534
535        out.startTag(null, "string-array");
536        if (name != null) {
537            out.attribute(null, "name", name);
538        }
539
540        final int N = val.length;
541        out.attribute(null, "num", Integer.toString(N));
542
543        for (int i=0; i<N; i++) {
544            out.startTag(null, "item");
545            out.attribute(null, "value", val[i]);
546            out.endTag(null, "item");
547        }
548
549        out.endTag(null, "string-array");
550    }
551
552    /**
553     * Flatten an object's value into an XmlSerializer.  The value can later
554     * be read back with readThisValueXml().
555     *
556     * Currently supported value types are: null, String, Integer, Long,
557     * Float, Double Boolean, Map, List.
558     *
559     * @param v The object to be flattened.
560     * @param name Name attribute to include with this value's tag, or null
561     *             for none.
562     * @param out XmlSerializer to write the object into.
563     *
564     * @see #writeMapXml
565     * @see #writeListXml
566     * @see #readValueXml
567     */
568    public static final void writeValueXml(Object v, String name, XmlSerializer out)
569            throws XmlPullParserException, java.io.IOException {
570        writeValueXml(v, name, out, null);
571    }
572
573    /**
574     * Flatten an object's value into an XmlSerializer.  The value can later
575     * be read back with readThisValueXml().
576     *
577     * Currently supported value types are: null, String, Integer, Long,
578     * Float, Double Boolean, Map, List.
579     *
580     * @param v The object to be flattened.
581     * @param name Name attribute to include with this value's tag, or null
582     *             for none.
583     * @param out XmlSerializer to write the object into.
584     * @param callback Handler for Object types not recognized.
585     *
586     * @see #writeMapXml
587     * @see #writeListXml
588     * @see #readValueXml
589     */
590    private static final void writeValueXml(Object v, String name, XmlSerializer out,
591            WriteMapCallback callback)  throws XmlPullParserException, java.io.IOException {
592        String typeStr;
593        if (v == null) {
594            out.startTag(null, "null");
595            if (name != null) {
596                out.attribute(null, "name", name);
597            }
598            out.endTag(null, "null");
599            return;
600        } else if (v instanceof String) {
601            out.startTag(null, "string");
602            if (name != null) {
603                out.attribute(null, "name", name);
604            }
605            out.text(v.toString());
606            out.endTag(null, "string");
607            return;
608        } else if (v instanceof Integer) {
609            typeStr = "int";
610        } else if (v instanceof Long) {
611            typeStr = "long";
612        } else if (v instanceof Float) {
613            typeStr = "float";
614        } else if (v instanceof Double) {
615            typeStr = "double";
616        } else if (v instanceof Boolean) {
617            typeStr = "boolean";
618        } else if (v instanceof byte[]) {
619            writeByteArrayXml((byte[])v, name, out);
620            return;
621        } else if (v instanceof int[]) {
622            writeIntArrayXml((int[])v, name, out);
623            return;
624        } else if (v instanceof long[]) {
625            writeLongArrayXml((long[])v, name, out);
626            return;
627        } else if (v instanceof double[]) {
628            writeDoubleArrayXml((double[])v, name, out);
629            return;
630        } else if (v instanceof String[]) {
631            writeStringArrayXml((String[])v, name, out);
632            return;
633        } else if (v instanceof Map) {
634            writeMapXml((Map)v, name, out);
635            return;
636        } else if (v instanceof List) {
637            writeListXml((List) v, name, out);
638            return;
639        } else if (v instanceof Set) {
640            writeSetXml((Set) v, name, out);
641            return;
642        } else if (v instanceof CharSequence) {
643            // XXX This is to allow us to at least write something if
644            // we encounter styled text...  but it means we will drop all
645            // of the styling information. :(
646            out.startTag(null, "string");
647            if (name != null) {
648                out.attribute(null, "name", name);
649            }
650            out.text(v.toString());
651            out.endTag(null, "string");
652            return;
653        } else if (callback != null) {
654            callback.writeUnknownObject(v, name, out);
655            return;
656        } else {
657            throw new RuntimeException("writeValueXml: unable to write value " + v);
658        }
659
660        out.startTag(null, typeStr);
661        if (name != null) {
662            out.attribute(null, "name", name);
663        }
664        out.attribute(null, "value", v.toString());
665        out.endTag(null, typeStr);
666    }
667
668    /**
669     * Read a HashMap from an InputStream containing XML.  The stream can
670     * previously have been written by writeMapXml().
671     *
672     * @param in The InputStream from which to read.
673     *
674     * @return HashMap The resulting map.
675     *
676     * @see #readListXml
677     * @see #readValueXml
678     * @see #readThisMapXml
679     * #see #writeMapXml
680     */
681    @SuppressWarnings("unchecked")
682    public static final HashMap<String, ?> readMapXml(InputStream in)
683    throws XmlPullParserException, java.io.IOException
684    {
685        XmlPullParser   parser = Xml.newPullParser();
686        parser.setInput(in, null);
687        return (HashMap<String, ?>) readValueXml(parser, new String[1]);
688    }
689
690    /**
691     * Read an ArrayList from an InputStream containing XML.  The stream can
692     * previously have been written by writeListXml().
693     *
694     * @param in The InputStream from which to read.
695     *
696     * @return ArrayList The resulting list.
697     *
698     * @see #readMapXml
699     * @see #readValueXml
700     * @see #readThisListXml
701     * @see #writeListXml
702     */
703    public static final ArrayList readListXml(InputStream in)
704    throws XmlPullParserException, java.io.IOException
705    {
706        XmlPullParser   parser = Xml.newPullParser();
707        parser.setInput(in, null);
708        return (ArrayList)readValueXml(parser, new String[1]);
709    }
710
711
712    /**
713     * Read a HashSet from an InputStream containing XML. The stream can
714     * previously have been written by writeSetXml().
715     *
716     * @param in The InputStream from which to read.
717     *
718     * @return HashSet The resulting set.
719     *
720     * @throws XmlPullParserException
721     * @throws java.io.IOException
722     *
723     * @see #readValueXml
724     * @see #readThisSetXml
725     * @see #writeSetXml
726     */
727    public static final HashSet readSetXml(InputStream in)
728            throws XmlPullParserException, java.io.IOException {
729        XmlPullParser parser = Xml.newPullParser();
730        parser.setInput(in, null);
731        return (HashSet) readValueXml(parser, new String[1]);
732    }
733
734    /**
735     * Read a HashMap object from an XmlPullParser.  The XML data could
736     * previously have been generated by writeMapXml().  The XmlPullParser
737     * must be positioned <em>after</em> the tag that begins the map.
738     *
739     * @param parser The XmlPullParser from which to read the map data.
740     * @param endTag Name of the tag that will end the map, usually "map".
741     * @param name An array of one string, used to return the name attribute
742     *             of the map's tag.
743     *
744     * @return HashMap The newly generated map.
745     *
746     * @see #readMapXml
747     */
748    public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
749            String[] name) throws XmlPullParserException, java.io.IOException {
750        return readThisMapXml(parser, endTag, name, null);
751    }
752
753    /**
754     * Read a HashMap object from an XmlPullParser.  The XML data could
755     * previously have been generated by writeMapXml().  The XmlPullParser
756     * must be positioned <em>after</em> the tag that begins the map.
757     *
758     * @param parser The XmlPullParser from which to read the map data.
759     * @param endTag Name of the tag that will end the map, usually "map".
760     * @param name An array of one string, used to return the name attribute
761     *             of the map's tag.
762     *
763     * @return HashMap The newly generated map.
764     *
765     * @see #readMapXml
766     * @hide
767     */
768    public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
769            String[] name, ReadMapCallback callback)
770            throws XmlPullParserException, java.io.IOException
771    {
772        HashMap<String, Object> map = new HashMap<String, Object>();
773
774        int eventType = parser.getEventType();
775        do {
776            if (eventType == parser.START_TAG) {
777                Object val = readThisValueXml(parser, name, callback);
778                map.put(name[0], val);
779            } else if (eventType == parser.END_TAG) {
780                if (parser.getName().equals(endTag)) {
781                    return map;
782                }
783                throw new XmlPullParserException(
784                    "Expected " + endTag + " end tag at: " + parser.getName());
785            }
786            eventType = parser.next();
787        } while (eventType != parser.END_DOCUMENT);
788
789        throw new XmlPullParserException(
790            "Document ended before " + endTag + " end tag");
791    }
792
793    /**
794     * Read an ArrayList object from an XmlPullParser.  The XML data could
795     * previously have been generated by writeListXml().  The XmlPullParser
796     * must be positioned <em>after</em> the tag that begins the list.
797     *
798     * @param parser The XmlPullParser from which to read the list data.
799     * @param endTag Name of the tag that will end the list, usually "list".
800     * @param name An array of one string, used to return the name attribute
801     *             of the list's tag.
802     *
803     * @return HashMap The newly generated list.
804     *
805     * @see #readListXml
806     */
807    public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
808            String[] name) throws XmlPullParserException, java.io.IOException {
809        return readThisListXml(parser, endTag, name, null);
810    }
811
812    /**
813     * Read an ArrayList object from an XmlPullParser.  The XML data could
814     * previously have been generated by writeListXml().  The XmlPullParser
815     * must be positioned <em>after</em> the tag that begins the list.
816     *
817     * @param parser The XmlPullParser from which to read the list data.
818     * @param endTag Name of the tag that will end the list, usually "list".
819     * @param name An array of one string, used to return the name attribute
820     *             of the list's tag.
821     *
822     * @return HashMap The newly generated list.
823     *
824     * @see #readListXml
825     */
826    private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
827            String[] name, ReadMapCallback callback)
828            throws XmlPullParserException, java.io.IOException {
829        ArrayList list = new ArrayList();
830
831        int eventType = parser.getEventType();
832        do {
833            if (eventType == parser.START_TAG) {
834                Object val = readThisValueXml(parser, name, callback);
835                list.add(val);
836                //System.out.println("Adding to list: " + val);
837            } else if (eventType == parser.END_TAG) {
838                if (parser.getName().equals(endTag)) {
839                    return list;
840                }
841                throw new XmlPullParserException(
842                    "Expected " + endTag + " end tag at: " + parser.getName());
843            }
844            eventType = parser.next();
845        } while (eventType != parser.END_DOCUMENT);
846
847        throw new XmlPullParserException(
848            "Document ended before " + endTag + " end tag");
849    }
850
851    /**
852     * Read a HashSet object from an XmlPullParser. The XML data could previously
853     * have been generated by writeSetXml(). The XmlPullParser must be positioned
854     * <em>after</em> the tag that begins the set.
855     *
856     * @param parser The XmlPullParser from which to read the set data.
857     * @param endTag Name of the tag that will end the set, usually "set".
858     * @param name An array of one string, used to return the name attribute
859     *             of the set's tag.
860     *
861     * @return HashSet The newly generated set.
862     *
863     * @throws XmlPullParserException
864     * @throws java.io.IOException
865     *
866     * @see #readSetXml
867     */
868    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
869            throws XmlPullParserException, java.io.IOException {
870        return readThisSetXml(parser, endTag, name, null);
871    }
872
873    /**
874     * Read a HashSet object from an XmlPullParser. The XML data could previously
875     * have been generated by writeSetXml(). The XmlPullParser must be positioned
876     * <em>after</em> the tag that begins the set.
877     *
878     * @param parser The XmlPullParser from which to read the set data.
879     * @param endTag Name of the tag that will end the set, usually "set".
880     * @param name An array of one string, used to return the name attribute
881     *             of the set's tag.
882     *
883     * @return HashSet The newly generated set.
884     *
885     * @throws XmlPullParserException
886     * @throws java.io.IOException
887     *
888     * @see #readSetXml
889     * @hide
890     */
891    private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
892            ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
893        HashSet set = new HashSet();
894
895        int eventType = parser.getEventType();
896        do {
897            if (eventType == parser.START_TAG) {
898                Object val = readThisValueXml(parser, name, callback);
899                set.add(val);
900                //System.out.println("Adding to set: " + val);
901            } else if (eventType == parser.END_TAG) {
902                if (parser.getName().equals(endTag)) {
903                    return set;
904                }
905                throw new XmlPullParserException(
906                        "Expected " + endTag + " end tag at: " + parser.getName());
907            }
908            eventType = parser.next();
909        } while (eventType != parser.END_DOCUMENT);
910
911        throw new XmlPullParserException(
912                "Document ended before " + endTag + " end tag");
913    }
914
915    /**
916     * Read an int[] object from an XmlPullParser.  The XML data could
917     * previously have been generated by writeIntArrayXml().  The XmlPullParser
918     * must be positioned <em>after</em> the tag that begins the list.
919     *
920     * @param parser The XmlPullParser from which to read the list data.
921     * @param endTag Name of the tag that will end the list, usually "list".
922     * @param name An array of one string, used to return the name attribute
923     *             of the list's tag.
924     *
925     * @return Returns a newly generated int[].
926     *
927     * @see #readListXml
928     */
929    public static final int[] readThisIntArrayXml(XmlPullParser parser,
930            String endTag, String[] name)
931            throws XmlPullParserException, java.io.IOException {
932
933        int num;
934        try {
935            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
936        } catch (NullPointerException e) {
937            throw new XmlPullParserException(
938                    "Need num attribute in byte-array");
939        } catch (NumberFormatException e) {
940            throw new XmlPullParserException(
941                    "Not a number in num attribute in byte-array");
942        }
943        parser.next();
944
945        int[] array = new int[num];
946        int i = 0;
947
948        int eventType = parser.getEventType();
949        do {
950            if (eventType == parser.START_TAG) {
951                if (parser.getName().equals("item")) {
952                    try {
953                        array[i] = Integer.parseInt(
954                                parser.getAttributeValue(null, "value"));
955                    } catch (NullPointerException e) {
956                        throw new XmlPullParserException(
957                                "Need value attribute in item");
958                    } catch (NumberFormatException e) {
959                        throw new XmlPullParserException(
960                                "Not a number in value attribute in item");
961                    }
962                } else {
963                    throw new XmlPullParserException(
964                            "Expected item tag at: " + parser.getName());
965                }
966            } else if (eventType == parser.END_TAG) {
967                if (parser.getName().equals(endTag)) {
968                    return array;
969                } else if (parser.getName().equals("item")) {
970                    i++;
971                } else {
972                    throw new XmlPullParserException(
973                        "Expected " + endTag + " end tag at: "
974                        + parser.getName());
975                }
976            }
977            eventType = parser.next();
978        } while (eventType != parser.END_DOCUMENT);
979
980        throw new XmlPullParserException(
981            "Document ended before " + endTag + " end tag");
982    }
983
984    /**
985     * Read a long[] object from an XmlPullParser.  The XML data could
986     * previously have been generated by writeLongArrayXml().  The XmlPullParser
987     * must be positioned <em>after</em> the tag that begins the list.
988     *
989     * @param parser The XmlPullParser from which to read the list data.
990     * @param endTag Name of the tag that will end the list, usually "list".
991     * @param name An array of one string, used to return the name attribute
992     *             of the list's tag.
993     *
994     * @return Returns a newly generated long[].
995     *
996     * @see #readListXml
997     */
998    public static final long[] readThisLongArrayXml(XmlPullParser parser,
999            String endTag, String[] name)
1000            throws XmlPullParserException, java.io.IOException {
1001
1002        int num;
1003        try {
1004            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1005        } catch (NullPointerException e) {
1006            throw new XmlPullParserException("Need num attribute in long-array");
1007        } catch (NumberFormatException e) {
1008            throw new XmlPullParserException("Not a number in num attribute in long-array");
1009        }
1010        parser.next();
1011
1012        long[] array = new long[num];
1013        int i = 0;
1014
1015        int eventType = parser.getEventType();
1016        do {
1017            if (eventType == parser.START_TAG) {
1018                if (parser.getName().equals("item")) {
1019                    try {
1020                        array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
1021                    } catch (NullPointerException e) {
1022                        throw new XmlPullParserException("Need value attribute in item");
1023                    } catch (NumberFormatException e) {
1024                        throw new XmlPullParserException("Not a number in value attribute in item");
1025                    }
1026                } else {
1027                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1028                }
1029            } else if (eventType == parser.END_TAG) {
1030                if (parser.getName().equals(endTag)) {
1031                    return array;
1032                } else if (parser.getName().equals("item")) {
1033                    i++;
1034                } else {
1035                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1036                            parser.getName());
1037                }
1038            }
1039            eventType = parser.next();
1040        } while (eventType != parser.END_DOCUMENT);
1041
1042        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1043    }
1044
1045    /**
1046     * Read a double[] object from an XmlPullParser.  The XML data could
1047     * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
1048     * must be positioned <em>after</em> the tag that begins the list.
1049     *
1050     * @param parser The XmlPullParser from which to read the list data.
1051     * @param endTag Name of the tag that will end the list, usually "double-array".
1052     * @param name An array of one string, used to return the name attribute
1053     *             of the list's tag.
1054     *
1055     * @return Returns a newly generated double[].
1056     *
1057     * @see #readListXml
1058     */
1059    public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
1060            String[] name) throws XmlPullParserException, java.io.IOException {
1061
1062        int num;
1063        try {
1064            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1065        } catch (NullPointerException e) {
1066            throw new XmlPullParserException("Need num attribute in double-array");
1067        } catch (NumberFormatException e) {
1068            throw new XmlPullParserException("Not a number in num attribute in double-array");
1069        }
1070        parser.next();
1071
1072        double[] array = new double[num];
1073        int i = 0;
1074
1075        int eventType = parser.getEventType();
1076        do {
1077            if (eventType == parser.START_TAG) {
1078                if (parser.getName().equals("item")) {
1079                    try {
1080                        array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
1081                    } catch (NullPointerException e) {
1082                        throw new XmlPullParserException("Need value attribute in item");
1083                    } catch (NumberFormatException e) {
1084                        throw new XmlPullParserException("Not a number in value attribute in item");
1085                    }
1086                } else {
1087                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1088                }
1089            } else if (eventType == parser.END_TAG) {
1090                if (parser.getName().equals(endTag)) {
1091                    return array;
1092                } else if (parser.getName().equals("item")) {
1093                    i++;
1094                } else {
1095                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1096                            parser.getName());
1097                }
1098            }
1099            eventType = parser.next();
1100        } while (eventType != parser.END_DOCUMENT);
1101
1102        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1103    }
1104
1105    /**
1106     * Read a String[] object from an XmlPullParser.  The XML data could
1107     * previously have been generated by writeStringArrayXml().  The XmlPullParser
1108     * must be positioned <em>after</em> the tag that begins the list.
1109     *
1110     * @param parser The XmlPullParser from which to read the list data.
1111     * @param endTag Name of the tag that will end the list, usually "string-array".
1112     * @param name An array of one string, used to return the name attribute
1113     *             of the list's tag.
1114     *
1115     * @return Returns a newly generated String[].
1116     *
1117     * @see #readListXml
1118     */
1119    public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
1120            String[] name) throws XmlPullParserException, java.io.IOException {
1121
1122        int num;
1123        try {
1124            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1125        } catch (NullPointerException e) {
1126            throw new XmlPullParserException("Need num attribute in string-array");
1127        } catch (NumberFormatException e) {
1128            throw new XmlPullParserException("Not a number in num attribute in string-array");
1129        }
1130        parser.next();
1131
1132        String[] array = new String[num];
1133        int i = 0;
1134
1135        int eventType = parser.getEventType();
1136        do {
1137            if (eventType == parser.START_TAG) {
1138                if (parser.getName().equals("item")) {
1139                    try {
1140                        array[i] = parser.getAttributeValue(null, "value");
1141                    } catch (NullPointerException e) {
1142                        throw new XmlPullParserException("Need value attribute in item");
1143                    } catch (NumberFormatException e) {
1144                        throw new XmlPullParserException("Not a number in value attribute in item");
1145                    }
1146                } else {
1147                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1148                }
1149            } else if (eventType == parser.END_TAG) {
1150                if (parser.getName().equals(endTag)) {
1151                    return array;
1152                } else if (parser.getName().equals("item")) {
1153                    i++;
1154                } else {
1155                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1156                            parser.getName());
1157                }
1158            }
1159            eventType = parser.next();
1160        } while (eventType != parser.END_DOCUMENT);
1161
1162        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1163    }
1164
1165    /**
1166     * Read a flattened object from an XmlPullParser.  The XML data could
1167     * previously have been written with writeMapXml(), writeListXml(), or
1168     * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
1169     * tag that defines the value.
1170     *
1171     * @param parser The XmlPullParser from which to read the object.
1172     * @param name An array of one string, used to return the name attribute
1173     *             of the value's tag.
1174     *
1175     * @return Object The newly generated value object.
1176     *
1177     * @see #readMapXml
1178     * @see #readListXml
1179     * @see #writeValueXml
1180     */
1181    public static final Object readValueXml(XmlPullParser parser, String[] name)
1182    throws XmlPullParserException, java.io.IOException
1183    {
1184        int eventType = parser.getEventType();
1185        do {
1186            if (eventType == parser.START_TAG) {
1187                return readThisValueXml(parser, name, null);
1188            } else if (eventType == parser.END_TAG) {
1189                throw new XmlPullParserException(
1190                    "Unexpected end tag at: " + parser.getName());
1191            } else if (eventType == parser.TEXT) {
1192                throw new XmlPullParserException(
1193                    "Unexpected text: " + parser.getText());
1194            }
1195            eventType = parser.next();
1196        } while (eventType != parser.END_DOCUMENT);
1197
1198        throw new XmlPullParserException(
1199            "Unexpected end of document");
1200    }
1201
1202    private static final Object readThisValueXml(XmlPullParser parser, String[] name,
1203            ReadMapCallback callback)  throws XmlPullParserException, java.io.IOException {
1204        final String valueName = parser.getAttributeValue(null, "name");
1205        final String tagName = parser.getName();
1206
1207        //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1208
1209        Object res;
1210
1211        if (tagName.equals("null")) {
1212            res = null;
1213        } else if (tagName.equals("string")) {
1214            String value = "";
1215            int eventType;
1216            while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1217                if (eventType == parser.END_TAG) {
1218                    if (parser.getName().equals("string")) {
1219                        name[0] = valueName;
1220                        //System.out.println("Returning value for " + valueName + ": " + value);
1221                        return value;
1222                    }
1223                    throw new XmlPullParserException(
1224                        "Unexpected end tag in <string>: " + parser.getName());
1225                } else if (eventType == parser.TEXT) {
1226                    value += parser.getText();
1227                } else if (eventType == parser.START_TAG) {
1228                    throw new XmlPullParserException(
1229                        "Unexpected start tag in <string>: " + parser.getName());
1230                }
1231            }
1232            throw new XmlPullParserException(
1233                "Unexpected end of document in <string>");
1234        } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1235            // all work already done by readThisPrimitiveValueXml
1236        } else if (tagName.equals("int-array")) {
1237            res = readThisIntArrayXml(parser, "int-array", name);
1238            name[0] = valueName;
1239            //System.out.println("Returning value for " + valueName + ": " + res);
1240            return res;
1241        } else if (tagName.equals("long-array")) {
1242            res = readThisLongArrayXml(parser, "long-array", name);
1243            name[0] = valueName;
1244            //System.out.println("Returning value for " + valueName + ": " + res);
1245            return res;
1246        } else if (tagName.equals("double-array")) {
1247            res = readThisDoubleArrayXml(parser, "double-array", name);
1248            name[0] = valueName;
1249            //System.out.println("Returning value for " + valueName + ": " + res);
1250            return res;
1251        } else if (tagName.equals("string-array")) {
1252            res = readThisStringArrayXml(parser, "string-array", name);
1253            name[0] = valueName;
1254            //System.out.println("Returning value for " + valueName + ": " + res);
1255            return res;
1256        } else if (tagName.equals("map")) {
1257            parser.next();
1258            res = readThisMapXml(parser, "map", name);
1259            name[0] = valueName;
1260            //System.out.println("Returning value for " + valueName + ": " + res);
1261            return res;
1262        } else if (tagName.equals("list")) {
1263            parser.next();
1264            res = readThisListXml(parser, "list", name);
1265            name[0] = valueName;
1266            //System.out.println("Returning value for " + valueName + ": " + res);
1267            return res;
1268        } else if (tagName.equals("set")) {
1269            parser.next();
1270            res = readThisSetXml(parser, "set", name);
1271            name[0] = valueName;
1272            //System.out.println("Returning value for " + valueName + ": " + res);
1273            return res;
1274        } else if (callback != null) {
1275            res = callback.readThisUnknownObjectXml(parser, tagName);
1276            name[0] = valueName;
1277            return res;
1278        } else {
1279            throw new XmlPullParserException("Unknown tag: " + tagName);
1280        }
1281
1282        // Skip through to end tag.
1283        int eventType;
1284        while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1285            if (eventType == parser.END_TAG) {
1286                if (parser.getName().equals(tagName)) {
1287                    name[0] = valueName;
1288                    //System.out.println("Returning value for " + valueName + ": " + res);
1289                    return res;
1290                }
1291                throw new XmlPullParserException(
1292                    "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1293            } else if (eventType == parser.TEXT) {
1294                throw new XmlPullParserException(
1295                "Unexpected text in <" + tagName + ">: " + parser.getName());
1296            } else if (eventType == parser.START_TAG) {
1297                throw new XmlPullParserException(
1298                    "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1299            }
1300        }
1301        throw new XmlPullParserException(
1302            "Unexpected end of document in <" + tagName + ">");
1303    }
1304
1305    private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
1306    throws XmlPullParserException, java.io.IOException
1307    {
1308        try {
1309            if (tagName.equals("int")) {
1310                return Integer.parseInt(parser.getAttributeValue(null, "value"));
1311            } else if (tagName.equals("long")) {
1312                return Long.valueOf(parser.getAttributeValue(null, "value"));
1313            } else if (tagName.equals("float")) {
1314                return new Float(parser.getAttributeValue(null, "value"));
1315            } else if (tagName.equals("double")) {
1316                return new Double(parser.getAttributeValue(null, "value"));
1317            } else if (tagName.equals("boolean")) {
1318                return Boolean.valueOf(parser.getAttributeValue(null, "value"));
1319            } else {
1320                return null;
1321            }
1322        } catch (NullPointerException e) {
1323            throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
1324        } catch (NumberFormatException e) {
1325            throw new XmlPullParserException(
1326                    "Not a number in value attribute in <" + tagName + ">");
1327        }
1328    }
1329
1330    public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1331    {
1332        int type;
1333        while ((type=parser.next()) != parser.START_TAG
1334                   && type != parser.END_DOCUMENT) {
1335            ;
1336        }
1337
1338        if (type != parser.START_TAG) {
1339            throw new XmlPullParserException("No start tag found");
1340        }
1341
1342        if (!parser.getName().equals(firstElementName)) {
1343            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1344                    ", expected " + firstElementName);
1345        }
1346    }
1347
1348    public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1349    {
1350        int type;
1351        while ((type=parser.next()) != parser.START_TAG
1352                   && type != parser.END_DOCUMENT) {
1353            ;
1354        }
1355    }
1356
1357    public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1358            throws IOException, XmlPullParserException {
1359        for (;;) {
1360            int type = parser.next();
1361            if (type == XmlPullParser.END_DOCUMENT
1362                    || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1363                return false;
1364            }
1365            if (type == XmlPullParser.START_TAG
1366                    && parser.getDepth() == outerDepth + 1) {
1367                return true;
1368            }
1369        }
1370    }
1371
1372    public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1373        final String value = in.getAttributeValue(null, name);
1374        try {
1375            return Integer.parseInt(value);
1376        } catch (NumberFormatException e) {
1377            return defaultValue;
1378        }
1379    }
1380
1381    public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1382        final String value = in.getAttributeValue(null, name);
1383        try {
1384            return Integer.parseInt(value);
1385        } catch (NumberFormatException e) {
1386            throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1387        }
1388    }
1389
1390    public static void writeIntAttribute(XmlSerializer out, String name, int value)
1391            throws IOException {
1392        out.attribute(null, name, Integer.toString(value));
1393    }
1394
1395    public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1396        final String value = in.getAttributeValue(null, name);
1397        try {
1398            return Long.parseLong(value);
1399        } catch (NumberFormatException e) {
1400            return defaultValue;
1401        }
1402    }
1403
1404    public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1405        final String value = in.getAttributeValue(null, name);
1406        try {
1407            return Long.parseLong(value);
1408        } catch (NumberFormatException e) {
1409            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1410        }
1411    }
1412
1413    public static void writeLongAttribute(XmlSerializer out, String name, long value)
1414            throws IOException {
1415        out.attribute(null, name, Long.toString(value));
1416    }
1417
1418    public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1419        final String value = in.getAttributeValue(null, name);
1420        return Boolean.parseBoolean(value);
1421    }
1422
1423    public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1424            throws IOException {
1425        out.attribute(null, name, Boolean.toString(value));
1426    }
1427
1428    /** @hide */
1429    public interface WriteMapCallback {
1430        /**
1431         * Called from writeMapXml when an Object type is not recognized. The implementer
1432         * must write out the entire element including start and end tags.
1433         *
1434         * @param v The object to be written out
1435         * @param name The mapping key for v. Must be written into the "name" attribute of the
1436         *             start tag.
1437         * @param out The XML output stream.
1438         * @throws XmlPullParserException on unrecognized Object type.
1439         * @throws IOException on XmlSerializer serialization errors.
1440         * @hide
1441         */
1442         public void writeUnknownObject(Object v, String name, XmlSerializer out)
1443                 throws XmlPullParserException, IOException;
1444    }
1445
1446    /** @hide */
1447    public interface ReadMapCallback {
1448        /**
1449         * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1450         * is positioned within the start tag so that attributes can be read using in.getAttribute.
1451         *
1452         * @param in the XML input stream
1453         * @param tag the START_TAG that was not recognized.
1454         * @return the Object parsed from the stream which will be put into the map.
1455         * @throws XmlPullParserException if the START_TAG is not recognized.
1456         * @throws IOException on XmlPullParser serialization errors.
1457         * @hide
1458         */
1459        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
1460                throws XmlPullParserException, IOException;
1461    }
1462}
1463