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