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.compat.annotation.UnsupportedAppUsage;
20 import android.graphics.Bitmap;
21 import android.graphics.Bitmap.CompressFormat;
22 import android.graphics.BitmapFactory;
23 import android.net.Uri;
24 import android.text.TextUtils;
25 import android.util.ArrayMap;
26 import android.util.Base64;
27 import android.util.Xml;
28 
29 import libcore.util.HexEncoding;
30 
31 import org.xmlpull.v1.XmlPullParser;
32 import org.xmlpull.v1.XmlPullParserException;
33 import org.xmlpull.v1.XmlSerializer;
34 
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.net.ProtocolException;
40 import java.nio.charset.StandardCharsets;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48 
49 /** {@hide} */
50 public class XmlUtils {
51 
52     private static final String STRING_ARRAY_SEPARATOR = ":";
53 
54     @UnsupportedAppUsage
skipCurrentTag(XmlPullParser parser)55     public static void skipCurrentTag(XmlPullParser parser)
56             throws XmlPullParserException, IOException {
57         int outerDepth = parser.getDepth();
58         int type;
59         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
60                && (type != XmlPullParser.END_TAG
61                        || parser.getDepth() > outerDepth)) {
62         }
63     }
64 
65     public static final int
convertValueToList(CharSequence value, String[] options, int defaultValue)66     convertValueToList(CharSequence value, String[] options, int defaultValue)
67     {
68         if (!TextUtils.isEmpty(value)) {
69             for (int i = 0; i < options.length; i++) {
70                 if (value.equals(options[i]))
71                     return i;
72             }
73         }
74 
75         return defaultValue;
76     }
77 
78     @UnsupportedAppUsage
79     public static final boolean
convertValueToBoolean(CharSequence value, boolean defaultValue)80     convertValueToBoolean(CharSequence value, boolean defaultValue)
81     {
82         boolean result = false;
83 
84         if (TextUtils.isEmpty(value)) {
85             return defaultValue;
86         }
87 
88         if (value.equals("1")
89         ||  value.equals("true")
90         ||  value.equals("TRUE"))
91             result = true;
92 
93         return result;
94     }
95 
96     @UnsupportedAppUsage
97     public static final int
convertValueToInt(CharSequence charSeq, int defaultValue)98     convertValueToInt(CharSequence charSeq, int defaultValue)
99     {
100         if (TextUtils.isEmpty(charSeq)) {
101             return defaultValue;
102         }
103 
104         String nm = charSeq.toString();
105 
106         // XXX This code is copied from Integer.decode() so we don't
107         // have to instantiate an Integer!
108 
109         int value;
110         int sign = 1;
111         int index = 0;
112         int len = nm.length();
113         int base = 10;
114 
115         if ('-' == nm.charAt(0)) {
116             sign = -1;
117             index++;
118         }
119 
120         if ('0' == nm.charAt(index)) {
121             //  Quick check for a zero by itself
122             if (index == (len - 1))
123                 return 0;
124 
125             char    c = nm.charAt(index + 1);
126 
127             if ('x' == c || 'X' == c) {
128                 index += 2;
129                 base = 16;
130             } else {
131                 index++;
132                 base = 8;
133             }
134         }
135         else if ('#' == nm.charAt(index))
136         {
137             index++;
138             base = 16;
139         }
140 
141         return Integer.parseInt(nm.substring(index), base) * sign;
142     }
143 
convertValueToUnsignedInt(String value, int defaultValue)144     public static int convertValueToUnsignedInt(String value, int defaultValue) {
145         if (TextUtils.isEmpty(value)) {
146             return defaultValue;
147         }
148 
149         return parseUnsignedIntAttribute(value);
150     }
151 
parseUnsignedIntAttribute(CharSequence charSeq)152     public static int parseUnsignedIntAttribute(CharSequence charSeq) {
153         String  value = charSeq.toString();
154 
155         long    bits;
156         int     index = 0;
157         int     len = value.length();
158         int     base = 10;
159 
160         if ('0' == value.charAt(index)) {
161             //  Quick check for zero by itself
162             if (index == (len - 1))
163                 return 0;
164 
165             char    c = value.charAt(index + 1);
166 
167             if ('x' == c || 'X' == c) {     //  check for hex
168                 index += 2;
169                 base = 16;
170             } else {                        //  check for octal
171                 index++;
172                 base = 8;
173             }
174         } else if ('#' == value.charAt(index)) {
175             index++;
176             base = 16;
177         }
178 
179         return (int) Long.parseLong(value.substring(index), base);
180     }
181 
182     /**
183      * Flatten a Map into an output stream as XML.  The map can later be
184      * read back with readMapXml().
185      *
186      * @param val The map to be flattened.
187      * @param out Where to write the XML data.
188      *
189      * @see #writeMapXml(Map, String, XmlSerializer)
190      * @see #writeListXml
191      * @see #writeValueXml
192      * @see #readMapXml
193      */
194     @UnsupportedAppUsage
writeMapXml(Map val, OutputStream out)195     public static final void writeMapXml(Map val, OutputStream out)
196             throws XmlPullParserException, java.io.IOException {
197         XmlSerializer serializer = new FastXmlSerializer();
198         serializer.setOutput(out, StandardCharsets.UTF_8.name());
199         serializer.startDocument(null, true);
200         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
201         writeMapXml(val, null, serializer);
202         serializer.endDocument();
203     }
204 
205     /**
206      * Flatten a List into an output stream as XML.  The list can later be
207      * read back with readListXml().
208      *
209      * @param val The list to be flattened.
210      * @param out Where to write the XML data.
211      *
212      * @see #writeListXml(List, String, XmlSerializer)
213      * @see #writeMapXml
214      * @see #writeValueXml
215      * @see #readListXml
216      */
writeListXml(List val, OutputStream out)217     public static final void writeListXml(List val, OutputStream out)
218     throws XmlPullParserException, java.io.IOException
219     {
220         XmlSerializer serializer = Xml.newSerializer();
221         serializer.setOutput(out, StandardCharsets.UTF_8.name());
222         serializer.startDocument(null, true);
223         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
224         writeListXml(val, null, serializer);
225         serializer.endDocument();
226     }
227 
228     /**
229      * Flatten a Map into an XmlSerializer.  The map can later be read back
230      * with readThisMapXml().
231      *
232      * @param val The map to be flattened.
233      * @param name Name attribute to include with this list's tag, or null for
234      *             none.
235      * @param out XmlSerializer to write the map into.
236      *
237      * @see #writeMapXml(Map, OutputStream)
238      * @see #writeListXml
239      * @see #writeValueXml
240      * @see #readMapXml
241      */
writeMapXml(Map val, String name, XmlSerializer out)242     public static final void writeMapXml(Map val, String name, XmlSerializer out)
243             throws XmlPullParserException, java.io.IOException {
244         writeMapXml(val, name, out, null);
245     }
246 
247     /**
248      * Flatten a Map into an XmlSerializer.  The map can later be read back
249      * with readThisMapXml().
250      *
251      * @param val The map to be flattened.
252      * @param name Name attribute to include with this list's tag, or null for
253      *             none.
254      * @param out XmlSerializer to write the map into.
255      * @param callback Method to call when an Object type is not recognized.
256      *
257      * @see #writeMapXml(Map, OutputStream)
258      * @see #writeListXml
259      * @see #writeValueXml
260      * @see #readMapXml
261      *
262      * @hide
263      */
writeMapXml(Map val, String name, XmlSerializer out, WriteMapCallback callback)264     public static final void writeMapXml(Map val, String name, XmlSerializer out,
265             WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
266 
267         if (val == null) {
268             out.startTag(null, "null");
269             out.endTag(null, "null");
270             return;
271         }
272 
273         out.startTag(null, "map");
274         if (name != null) {
275             out.attribute(null, "name", name);
276         }
277 
278         writeMapXml(val, out, callback);
279 
280         out.endTag(null, "map");
281     }
282 
283     /**
284      * Flatten a Map into an XmlSerializer.  The map can later be read back
285      * with readThisMapXml(). This method presumes that the start tag and
286      * name attribute have already been written and does not write an end tag.
287      *
288      * @param val The map to be flattened.
289      * @param out XmlSerializer to write the map into.
290      *
291      * @see #writeMapXml(Map, OutputStream)
292      * @see #writeListXml
293      * @see #writeValueXml
294      * @see #readMapXml
295      *
296      * @hide
297      */
writeMapXml(Map val, XmlSerializer out, WriteMapCallback callback)298     public static final void writeMapXml(Map val, XmlSerializer out,
299             WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
300         if (val == null) {
301             return;
302         }
303 
304         Set s = val.entrySet();
305         Iterator i = s.iterator();
306 
307         while (i.hasNext()) {
308             Map.Entry e = (Map.Entry)i.next();
309             writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
310         }
311     }
312 
313     /**
314      * Flatten a List into an XmlSerializer.  The list can later be read back
315      * with readThisListXml().
316      *
317      * @param val The list to be flattened.
318      * @param name Name attribute to include with this list's tag, or null for
319      *             none.
320      * @param out XmlSerializer to write the list into.
321      *
322      * @see #writeListXml(List, OutputStream)
323      * @see #writeMapXml
324      * @see #writeValueXml
325      * @see #readListXml
326      */
writeListXml(List val, String name, XmlSerializer out)327     public static final void writeListXml(List val, String name, XmlSerializer out)
328     throws XmlPullParserException, java.io.IOException
329     {
330         if (val == null) {
331             out.startTag(null, "null");
332             out.endTag(null, "null");
333             return;
334         }
335 
336         out.startTag(null, "list");
337         if (name != null) {
338             out.attribute(null, "name", name);
339         }
340 
341         int N = val.size();
342         int i=0;
343         while (i < N) {
344             writeValueXml(val.get(i), null, out);
345             i++;
346         }
347 
348         out.endTag(null, "list");
349     }
350 
writeSetXml(Set val, String name, XmlSerializer out)351     public static final void writeSetXml(Set val, String name, XmlSerializer out)
352             throws XmlPullParserException, java.io.IOException {
353         if (val == null) {
354             out.startTag(null, "null");
355             out.endTag(null, "null");
356             return;
357         }
358 
359         out.startTag(null, "set");
360         if (name != null) {
361             out.attribute(null, "name", name);
362         }
363 
364         for (Object v : val) {
365             writeValueXml(v, null, out);
366         }
367 
368         out.endTag(null, "set");
369     }
370 
371     /**
372      * Flatten a byte[] into an XmlSerializer.  The list can later be read back
373      * with readThisByteArrayXml().
374      *
375      * @param val The byte array to be flattened.
376      * @param name Name attribute to include with this array's tag, or null for
377      *             none.
378      * @param out XmlSerializer to write the array into.
379      *
380      * @see #writeMapXml
381      * @see #writeValueXml
382      */
writeByteArrayXml(byte[] val, String name, XmlSerializer out)383     public static final void writeByteArrayXml(byte[] val, String name,
384             XmlSerializer out)
385             throws XmlPullParserException, java.io.IOException {
386 
387         if (val == null) {
388             out.startTag(null, "null");
389             out.endTag(null, "null");
390             return;
391         }
392 
393         out.startTag(null, "byte-array");
394         if (name != null) {
395             out.attribute(null, "name", name);
396         }
397 
398         final int N = val.length;
399         out.attribute(null, "num", Integer.toString(N));
400 
401         out.text(HexEncoding.encodeToString(val).toLowerCase());
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")
735     @UnsupportedAppUsage
readMapXml(InputStream in)736     public static final HashMap<String, ?> readMapXml(InputStream in)
737     throws XmlPullParserException, java.io.IOException
738     {
739         XmlPullParser   parser = Xml.newPullParser();
740         parser.setInput(in, StandardCharsets.UTF_8.name());
741         return (HashMap<String, ?>) readValueXml(parser, new String[1]);
742     }
743 
744     /**
745      * Read an ArrayList from an InputStream containing XML.  The stream can
746      * previously have been written by writeListXml().
747      *
748      * @param in The InputStream from which to read.
749      *
750      * @return ArrayList The resulting list.
751      *
752      * @see #readMapXml
753      * @see #readValueXml
754      * @see #readThisListXml
755      * @see #writeListXml
756      */
readListXml(InputStream in)757     public static final ArrayList readListXml(InputStream in)
758     throws XmlPullParserException, java.io.IOException
759     {
760         XmlPullParser   parser = Xml.newPullParser();
761         parser.setInput(in, StandardCharsets.UTF_8.name());
762         return (ArrayList)readValueXml(parser, new String[1]);
763     }
764 
765 
766     /**
767      * Read a HashSet from an InputStream containing XML. The stream can
768      * previously have been written by writeSetXml().
769      *
770      * @param in The InputStream from which to read.
771      *
772      * @return HashSet The resulting set.
773      *
774      * @throws XmlPullParserException
775      * @throws java.io.IOException
776      *
777      * @see #readValueXml
778      * @see #readThisSetXml
779      * @see #writeSetXml
780      */
readSetXml(InputStream in)781     public static final HashSet readSetXml(InputStream in)
782             throws XmlPullParserException, java.io.IOException {
783         XmlPullParser parser = Xml.newPullParser();
784         parser.setInput(in, null);
785         return (HashSet) readValueXml(parser, new String[1]);
786     }
787 
788     /**
789      * Read a HashMap object from an XmlPullParser.  The XML data could
790      * previously have been generated by writeMapXml().  The XmlPullParser
791      * must be positioned <em>after</em> the tag that begins the map.
792      *
793      * @param parser The XmlPullParser from which to read the map data.
794      * @param endTag Name of the tag that will end the map, usually "map".
795      * @param name An array of one string, used to return the name attribute
796      *             of the map's tag.
797      *
798      * @return HashMap The newly generated map.
799      *
800      * @see #readMapXml
801      */
readThisMapXml(XmlPullParser parser, String endTag, String[] name)802     public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
803             String[] name) throws XmlPullParserException, java.io.IOException {
804         return readThisMapXml(parser, endTag, name, null);
805     }
806 
807     /**
808      * Read a HashMap object from an XmlPullParser.  The XML data could
809      * previously have been generated by writeMapXml().  The XmlPullParser
810      * must be positioned <em>after</em> the tag that begins the map.
811      *
812      * @param parser The XmlPullParser from which to read the map data.
813      * @param endTag Name of the tag that will end the map, usually "map".
814      * @param name An array of one string, used to return the name attribute
815      *             of the map's tag.
816      *
817      * @return HashMap The newly generated map.
818      *
819      * @see #readMapXml
820      * @hide
821      */
readThisMapXml(XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback)822     public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
823             String[] name, ReadMapCallback callback)
824             throws XmlPullParserException, java.io.IOException
825     {
826         HashMap<String, Object> map = new HashMap<String, Object>();
827 
828         int eventType = parser.getEventType();
829         do {
830             if (eventType == parser.START_TAG) {
831                 Object val = readThisValueXml(parser, name, callback, false);
832                 map.put(name[0], val);
833             } else if (eventType == parser.END_TAG) {
834                 if (parser.getName().equals(endTag)) {
835                     return map;
836                 }
837                 throw new XmlPullParserException(
838                     "Expected " + endTag + " end tag at: " + parser.getName());
839             }
840             eventType = parser.next();
841         } while (eventType != parser.END_DOCUMENT);
842 
843         throw new XmlPullParserException(
844             "Document ended before " + endTag + " end tag");
845     }
846 
847     /**
848      * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap.
849      * @hide
850      */
readThisArrayMapXml(XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback)851     public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag,
852             String[] name, ReadMapCallback callback)
853             throws XmlPullParserException, java.io.IOException
854     {
855         ArrayMap<String, Object> map = new ArrayMap<>();
856 
857         int eventType = parser.getEventType();
858         do {
859             if (eventType == parser.START_TAG) {
860                 Object val = readThisValueXml(parser, name, callback, true);
861                 map.put(name[0], val);
862             } else if (eventType == parser.END_TAG) {
863                 if (parser.getName().equals(endTag)) {
864                     return map;
865                 }
866                 throw new XmlPullParserException(
867                     "Expected " + endTag + " end tag at: " + parser.getName());
868             }
869             eventType = parser.next();
870         } while (eventType != parser.END_DOCUMENT);
871 
872         throw new XmlPullParserException(
873             "Document ended before " + endTag + " end tag");
874     }
875 
876     /**
877      * Read an ArrayList object from an XmlPullParser.  The XML data could
878      * previously have been generated by writeListXml().  The XmlPullParser
879      * must be positioned <em>after</em> the tag that begins the list.
880      *
881      * @param parser The XmlPullParser from which to read the list data.
882      * @param endTag Name of the tag that will end the list, usually "list".
883      * @param name An array of one string, used to return the name attribute
884      *             of the list's tag.
885      *
886      * @return HashMap The newly generated list.
887      *
888      * @see #readListXml
889      */
readThisListXml(XmlPullParser parser, String endTag, String[] name)890     public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
891             String[] name) throws XmlPullParserException, java.io.IOException {
892         return readThisListXml(parser, endTag, name, null, false);
893     }
894 
895     /**
896      * Read an ArrayList object from an XmlPullParser.  The XML data could
897      * previously have been generated by writeListXml().  The XmlPullParser
898      * must be positioned <em>after</em> the tag that begins the list.
899      *
900      * @param parser The XmlPullParser from which to read the list data.
901      * @param endTag Name of the tag that will end the list, usually "list".
902      * @param name An array of one string, used to return the name attribute
903      *             of the list's tag.
904      *
905      * @return HashMap The newly generated list.
906      *
907      * @see #readListXml
908      */
readThisListXml(XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback, boolean arrayMap)909     private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
910             String[] name, ReadMapCallback callback, boolean arrayMap)
911             throws XmlPullParserException, java.io.IOException {
912         ArrayList list = new ArrayList();
913 
914         int eventType = parser.getEventType();
915         do {
916             if (eventType == parser.START_TAG) {
917                 Object val = readThisValueXml(parser, name, callback, arrayMap);
918                 list.add(val);
919                 //System.out.println("Adding to list: " + val);
920             } else if (eventType == parser.END_TAG) {
921                 if (parser.getName().equals(endTag)) {
922                     return list;
923                 }
924                 throw new XmlPullParserException(
925                     "Expected " + endTag + " end tag at: " + parser.getName());
926             }
927             eventType = parser.next();
928         } while (eventType != parser.END_DOCUMENT);
929 
930         throw new XmlPullParserException(
931             "Document ended before " + endTag + " end tag");
932     }
933 
934     /**
935      * Read a HashSet object from an XmlPullParser. The XML data could previously
936      * have been generated by writeSetXml(). The XmlPullParser must be positioned
937      * <em>after</em> the tag that begins the set.
938      *
939      * @param parser The XmlPullParser from which to read the set data.
940      * @param endTag Name of the tag that will end the set, usually "set".
941      * @param name An array of one string, used to return the name attribute
942      *             of the set's tag.
943      *
944      * @return HashSet The newly generated set.
945      *
946      * @throws XmlPullParserException
947      * @throws java.io.IOException
948      *
949      * @see #readSetXml
950      */
readThisSetXml(XmlPullParser parser, String endTag, String[] name)951     public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
952             throws XmlPullParserException, java.io.IOException {
953         return readThisSetXml(parser, endTag, name, null, false);
954     }
955 
956     /**
957      * Read a HashSet object from an XmlPullParser. The XML data could previously
958      * have been generated by writeSetXml(). The XmlPullParser must be positioned
959      * <em>after</em> the tag that begins the set.
960      *
961      * @param parser The XmlPullParser from which to read the set data.
962      * @param endTag Name of the tag that will end the set, usually "set".
963      * @param name An array of one string, used to return the name attribute
964      *             of the set's tag.
965      *
966      * @return HashSet The newly generated set.
967      *
968      * @throws XmlPullParserException
969      * @throws java.io.IOException
970      *
971      * @see #readSetXml
972      * @hide
973      */
readThisSetXml(XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback, boolean arrayMap)974     private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
975             ReadMapCallback callback, boolean arrayMap)
976             throws XmlPullParserException, java.io.IOException {
977         HashSet set = new HashSet();
978 
979         int eventType = parser.getEventType();
980         do {
981             if (eventType == parser.START_TAG) {
982                 Object val = readThisValueXml(parser, name, callback, arrayMap);
983                 set.add(val);
984                 //System.out.println("Adding to set: " + val);
985             } else if (eventType == parser.END_TAG) {
986                 if (parser.getName().equals(endTag)) {
987                     return set;
988                 }
989                 throw new XmlPullParserException(
990                         "Expected " + endTag + " end tag at: " + parser.getName());
991             }
992             eventType = parser.next();
993         } while (eventType != parser.END_DOCUMENT);
994 
995         throw new XmlPullParserException(
996                 "Document ended before " + endTag + " end tag");
997     }
998 
999     /**
1000      * Read a byte[] object from an XmlPullParser.  The XML data could
1001      * previously have been generated by writeByteArrayXml().  The XmlPullParser
1002      * must be positioned <em>after</em> the tag that begins the list.
1003      *
1004      * @param parser The XmlPullParser from which to read the list data.
1005      * @param endTag Name of the tag that will end the list, usually "list".
1006      * @param name An array of one string, used to return the name attribute
1007      *             of the list's tag.
1008      *
1009      * @return Returns a newly generated byte[].
1010      *
1011      * @see #writeByteArrayXml
1012      */
readThisByteArrayXml(XmlPullParser parser, String endTag, String[] name)1013     public static final byte[] readThisByteArrayXml(XmlPullParser parser,
1014             String endTag, String[] name)
1015             throws XmlPullParserException, java.io.IOException {
1016 
1017         int num;
1018         try {
1019             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1020         } catch (NullPointerException e) {
1021             throw new XmlPullParserException(
1022                     "Need num attribute in byte-array");
1023         } catch (NumberFormatException e) {
1024             throw new XmlPullParserException(
1025                     "Not a number in num attribute in byte-array");
1026         }
1027 
1028         // 0 len byte array does not have a text in the XML tag. So, initialize to 0 len array.
1029         // For all other array lens, HexEncoding.decode() below overrides the array.
1030         byte[] array = new byte[0];
1031 
1032         int eventType = parser.getEventType();
1033         do {
1034             if (eventType == parser.TEXT) {
1035                 if (num > 0) {
1036                     String values = parser.getText();
1037                     if (values == null || values.length() != num * 2) {
1038                         throw new XmlPullParserException(
1039                                 "Invalid value found in byte-array: " + values);
1040                     }
1041                     array = HexEncoding.decode(values);
1042                 }
1043             } else if (eventType == parser.END_TAG) {
1044                 if (parser.getName().equals(endTag)) {
1045                     return array;
1046                 } else {
1047                     throw new XmlPullParserException(
1048                             "Expected " + endTag + " end tag at: "
1049                                     + parser.getName());
1050                 }
1051             }
1052             eventType = parser.next();
1053         } while (eventType != parser.END_DOCUMENT);
1054 
1055         throw new XmlPullParserException(
1056                 "Document ended before " + endTag + " end tag");
1057     }
1058 
1059     /**
1060      * Read an int[] object from an XmlPullParser.  The XML data could
1061      * previously have been generated by writeIntArrayXml().  The XmlPullParser
1062      * must be positioned <em>after</em> the tag that begins the list.
1063      *
1064      * @param parser The XmlPullParser from which to read the list data.
1065      * @param endTag Name of the tag that will end the list, usually "list".
1066      * @param name An array of one string, used to return the name attribute
1067      *             of the list's tag.
1068      *
1069      * @return Returns a newly generated int[].
1070      *
1071      * @see #readListXml
1072      */
readThisIntArrayXml(XmlPullParser parser, String endTag, String[] name)1073     public static final int[] readThisIntArrayXml(XmlPullParser parser,
1074             String endTag, String[] name)
1075             throws XmlPullParserException, java.io.IOException {
1076 
1077         int num;
1078         try {
1079             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1080         } catch (NullPointerException e) {
1081             throw new XmlPullParserException(
1082                     "Need num attribute in int-array");
1083         } catch (NumberFormatException e) {
1084             throw new XmlPullParserException(
1085                     "Not a number in num attribute in int-array");
1086         }
1087         parser.next();
1088 
1089         int[] array = new int[num];
1090         int i = 0;
1091 
1092         int eventType = parser.getEventType();
1093         do {
1094             if (eventType == parser.START_TAG) {
1095                 if (parser.getName().equals("item")) {
1096                     try {
1097                         array[i] = Integer.parseInt(
1098                                 parser.getAttributeValue(null, "value"));
1099                     } catch (NullPointerException e) {
1100                         throw new XmlPullParserException(
1101                                 "Need value attribute in item");
1102                     } catch (NumberFormatException e) {
1103                         throw new XmlPullParserException(
1104                                 "Not a number in value attribute in item");
1105                     }
1106                 } else {
1107                     throw new XmlPullParserException(
1108                             "Expected item tag at: " + parser.getName());
1109                 }
1110             } else if (eventType == parser.END_TAG) {
1111                 if (parser.getName().equals(endTag)) {
1112                     return array;
1113                 } else if (parser.getName().equals("item")) {
1114                     i++;
1115                 } else {
1116                     throw new XmlPullParserException(
1117                         "Expected " + endTag + " end tag at: "
1118                         + parser.getName());
1119                 }
1120             }
1121             eventType = parser.next();
1122         } while (eventType != parser.END_DOCUMENT);
1123 
1124         throw new XmlPullParserException(
1125             "Document ended before " + endTag + " end tag");
1126     }
1127 
1128     /**
1129      * Read a long[] object from an XmlPullParser.  The XML data could
1130      * previously have been generated by writeLongArrayXml().  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 "list".
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 long[].
1139      *
1140      * @see #readListXml
1141      */
readThisLongArrayXml(XmlPullParser parser, String endTag, String[] name)1142     public static final long[] readThisLongArrayXml(XmlPullParser parser,
1143             String endTag, String[] name)
1144             throws XmlPullParserException, java.io.IOException {
1145 
1146         int num;
1147         try {
1148             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1149         } catch (NullPointerException e) {
1150             throw new XmlPullParserException("Need num attribute in long-array");
1151         } catch (NumberFormatException e) {
1152             throw new XmlPullParserException("Not a number in num attribute in long-array");
1153         }
1154         parser.next();
1155 
1156         long[] array = new long[num];
1157         int i = 0;
1158 
1159         int eventType = parser.getEventType();
1160         do {
1161             if (eventType == parser.START_TAG) {
1162                 if (parser.getName().equals("item")) {
1163                     try {
1164                         array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
1165                     } catch (NullPointerException e) {
1166                         throw new XmlPullParserException("Need value attribute in item");
1167                     } catch (NumberFormatException e) {
1168                         throw new XmlPullParserException("Not a number in value attribute in item");
1169                     }
1170                 } else {
1171                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1172                 }
1173             } else if (eventType == parser.END_TAG) {
1174                 if (parser.getName().equals(endTag)) {
1175                     return array;
1176                 } else if (parser.getName().equals("item")) {
1177                     i++;
1178                 } else {
1179                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1180                             parser.getName());
1181                 }
1182             }
1183             eventType = parser.next();
1184         } while (eventType != parser.END_DOCUMENT);
1185 
1186         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1187     }
1188 
1189     /**
1190      * Read a double[] object from an XmlPullParser.  The XML data could
1191      * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
1192      * must be positioned <em>after</em> the tag that begins the list.
1193      *
1194      * @param parser The XmlPullParser from which to read the list data.
1195      * @param endTag Name of the tag that will end the list, usually "double-array".
1196      * @param name An array of one string, used to return the name attribute
1197      *             of the list's tag.
1198      *
1199      * @return Returns a newly generated double[].
1200      *
1201      * @see #readListXml
1202      */
readThisDoubleArrayXml(XmlPullParser parser, String endTag, String[] name)1203     public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
1204             String[] name) throws XmlPullParserException, java.io.IOException {
1205 
1206         int num;
1207         try {
1208             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1209         } catch (NullPointerException e) {
1210             throw new XmlPullParserException("Need num attribute in double-array");
1211         } catch (NumberFormatException e) {
1212             throw new XmlPullParserException("Not a number in num attribute in double-array");
1213         }
1214         parser.next();
1215 
1216         double[] array = new double[num];
1217         int i = 0;
1218 
1219         int eventType = parser.getEventType();
1220         do {
1221             if (eventType == parser.START_TAG) {
1222                 if (parser.getName().equals("item")) {
1223                     try {
1224                         array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
1225                     } catch (NullPointerException e) {
1226                         throw new XmlPullParserException("Need value attribute in item");
1227                     } catch (NumberFormatException e) {
1228                         throw new XmlPullParserException("Not a number in value attribute in item");
1229                     }
1230                 } else {
1231                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1232                 }
1233             } else if (eventType == parser.END_TAG) {
1234                 if (parser.getName().equals(endTag)) {
1235                     return array;
1236                 } else if (parser.getName().equals("item")) {
1237                     i++;
1238                 } else {
1239                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1240                             parser.getName());
1241                 }
1242             }
1243             eventType = parser.next();
1244         } while (eventType != parser.END_DOCUMENT);
1245 
1246         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1247     }
1248 
1249     /**
1250      * Read a String[] object from an XmlPullParser.  The XML data could
1251      * previously have been generated by writeStringArrayXml().  The XmlPullParser
1252      * must be positioned <em>after</em> the tag that begins the list.
1253      *
1254      * @param parser The XmlPullParser from which to read the list data.
1255      * @param endTag Name of the tag that will end the list, usually "string-array".
1256      * @param name An array of one string, used to return the name attribute
1257      *             of the list's tag.
1258      *
1259      * @return Returns a newly generated String[].
1260      *
1261      * @see #readListXml
1262      */
readThisStringArrayXml(XmlPullParser parser, String endTag, String[] name)1263     public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
1264             String[] name) throws XmlPullParserException, java.io.IOException {
1265 
1266         int num;
1267         try {
1268             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1269         } catch (NullPointerException e) {
1270             throw new XmlPullParserException("Need num attribute in string-array");
1271         } catch (NumberFormatException e) {
1272             throw new XmlPullParserException("Not a number in num attribute in string-array");
1273         }
1274         parser.next();
1275 
1276         String[] array = new String[num];
1277         int i = 0;
1278 
1279         int eventType = parser.getEventType();
1280         do {
1281             if (eventType == parser.START_TAG) {
1282                 if (parser.getName().equals("item")) {
1283                     try {
1284                         array[i] = parser.getAttributeValue(null, "value");
1285                     } catch (NullPointerException e) {
1286                         throw new XmlPullParserException("Need value attribute in item");
1287                     } catch (NumberFormatException e) {
1288                         throw new XmlPullParserException("Not a number in value attribute in item");
1289                     }
1290                 } else {
1291                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1292                 }
1293             } else if (eventType == parser.END_TAG) {
1294                 if (parser.getName().equals(endTag)) {
1295                     return array;
1296                 } else if (parser.getName().equals("item")) {
1297                     i++;
1298                 } else {
1299                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1300                             parser.getName());
1301                 }
1302             }
1303             eventType = parser.next();
1304         } while (eventType != parser.END_DOCUMENT);
1305 
1306         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1307     }
1308 
1309     /**
1310      * Read a boolean[] object from an XmlPullParser.  The XML data could
1311      * previously have been generated by writeBooleanArrayXml().  The XmlPullParser
1312      * must be positioned <em>after</em> the tag that begins the list.
1313      *
1314      * @param parser The XmlPullParser from which to read the list data.
1315      * @param endTag Name of the tag that will end the list, usually "string-array".
1316      * @param name An array of one string, used to return the name attribute
1317      *             of the list's tag.
1318      *
1319      * @return Returns a newly generated boolean[].
1320      *
1321      * @see #readListXml
1322      */
readThisBooleanArrayXml(XmlPullParser parser, String endTag, String[] name)1323     public static final boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag,
1324             String[] name) throws XmlPullParserException, java.io.IOException {
1325 
1326         int num;
1327         try {
1328             num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1329         } catch (NullPointerException e) {
1330             throw new XmlPullParserException("Need num attribute in string-array");
1331         } catch (NumberFormatException e) {
1332             throw new XmlPullParserException("Not a number in num attribute in string-array");
1333         }
1334         parser.next();
1335 
1336         boolean[] array = new boolean[num];
1337         int i = 0;
1338 
1339         int eventType = parser.getEventType();
1340         do {
1341             if (eventType == parser.START_TAG) {
1342                 if (parser.getName().equals("item")) {
1343                     try {
1344                         array[i] = Boolean.parseBoolean(parser.getAttributeValue(null, "value"));
1345                     } catch (NullPointerException e) {
1346                         throw new XmlPullParserException("Need value attribute in item");
1347                     } catch (NumberFormatException e) {
1348                         throw new XmlPullParserException("Not a number in value attribute in item");
1349                     }
1350                 } else {
1351                     throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1352                 }
1353             } else if (eventType == parser.END_TAG) {
1354                 if (parser.getName().equals(endTag)) {
1355                     return array;
1356                 } else if (parser.getName().equals("item")) {
1357                     i++;
1358                 } else {
1359                     throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1360                             parser.getName());
1361                 }
1362             }
1363             eventType = parser.next();
1364         } while (eventType != parser.END_DOCUMENT);
1365 
1366         throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1367     }
1368 
1369     /**
1370      * Read a flattened object from an XmlPullParser.  The XML data could
1371      * previously have been written with writeMapXml(), writeListXml(), or
1372      * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
1373      * tag that defines the value.
1374      *
1375      * @param parser The XmlPullParser from which to read the object.
1376      * @param name An array of one string, used to return the name attribute
1377      *             of the value's tag.
1378      *
1379      * @return Object The newly generated value object.
1380      *
1381      * @see #readMapXml
1382      * @see #readListXml
1383      * @see #writeValueXml
1384      */
readValueXml(XmlPullParser parser, String[] name)1385     public static final Object readValueXml(XmlPullParser parser, String[] name)
1386     throws XmlPullParserException, java.io.IOException
1387     {
1388         int eventType = parser.getEventType();
1389         do {
1390             if (eventType == parser.START_TAG) {
1391                 return readThisValueXml(parser, name, null, false);
1392             } else if (eventType == parser.END_TAG) {
1393                 throw new XmlPullParserException(
1394                     "Unexpected end tag at: " + parser.getName());
1395             } else if (eventType == parser.TEXT) {
1396                 throw new XmlPullParserException(
1397                     "Unexpected text: " + parser.getText());
1398             }
1399             eventType = parser.next();
1400         } while (eventType != parser.END_DOCUMENT);
1401 
1402         throw new XmlPullParserException(
1403             "Unexpected end of document");
1404     }
1405 
readThisValueXml(XmlPullParser parser, String[] name, ReadMapCallback callback, boolean arrayMap)1406     private static final Object readThisValueXml(XmlPullParser parser, String[] name,
1407             ReadMapCallback callback, boolean arrayMap)
1408             throws XmlPullParserException, java.io.IOException {
1409         final String valueName = parser.getAttributeValue(null, "name");
1410         final String tagName = parser.getName();
1411 
1412         //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1413 
1414         Object res;
1415 
1416         if (tagName.equals("null")) {
1417             res = null;
1418         } else if (tagName.equals("string")) {
1419             String value = "";
1420             int eventType;
1421             while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1422                 if (eventType == parser.END_TAG) {
1423                     if (parser.getName().equals("string")) {
1424                         name[0] = valueName;
1425                         //System.out.println("Returning value for " + valueName + ": " + value);
1426                         return value;
1427                     }
1428                     throw new XmlPullParserException(
1429                         "Unexpected end tag in <string>: " + parser.getName());
1430                 } else if (eventType == parser.TEXT) {
1431                     value += parser.getText();
1432                 } else if (eventType == parser.START_TAG) {
1433                     throw new XmlPullParserException(
1434                         "Unexpected start tag in <string>: " + parser.getName());
1435                 }
1436             }
1437             throw new XmlPullParserException(
1438                 "Unexpected end of document in <string>");
1439         } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1440             // all work already done by readThisPrimitiveValueXml
1441         } else if (tagName.equals("byte-array")) {
1442             res = readThisByteArrayXml(parser, "byte-array", name);
1443             name[0] = valueName;
1444             //System.out.println("Returning value for " + valueName + ": " + res);
1445             return res;
1446         } else if (tagName.equals("int-array")) {
1447             res = readThisIntArrayXml(parser, "int-array", name);
1448             name[0] = valueName;
1449             //System.out.println("Returning value for " + valueName + ": " + res);
1450             return res;
1451         } else if (tagName.equals("long-array")) {
1452             res = readThisLongArrayXml(parser, "long-array", name);
1453             name[0] = valueName;
1454             //System.out.println("Returning value for " + valueName + ": " + res);
1455             return res;
1456         } else if (tagName.equals("double-array")) {
1457             res = readThisDoubleArrayXml(parser, "double-array", name);
1458             name[0] = valueName;
1459             //System.out.println("Returning value for " + valueName + ": " + res);
1460             return res;
1461         } else if (tagName.equals("string-array")) {
1462             res = readThisStringArrayXml(parser, "string-array", name);
1463             name[0] = valueName;
1464             //System.out.println("Returning value for " + valueName + ": " + res);
1465             return res;
1466         } else if (tagName.equals("boolean-array")) {
1467             res = readThisBooleanArrayXml(parser, "boolean-array", name);
1468             name[0] = valueName;
1469             //System.out.println("Returning value for " + valueName + ": " + res);
1470             return res;
1471         } else if (tagName.equals("map")) {
1472             parser.next();
1473             res = arrayMap
1474                     ? readThisArrayMapXml(parser, "map", name, callback)
1475                     : readThisMapXml(parser, "map", name, callback);
1476             name[0] = valueName;
1477             //System.out.println("Returning value for " + valueName + ": " + res);
1478             return res;
1479         } else if (tagName.equals("list")) {
1480             parser.next();
1481             res = readThisListXml(parser, "list", name, callback, arrayMap);
1482             name[0] = valueName;
1483             //System.out.println("Returning value for " + valueName + ": " + res);
1484             return res;
1485         } else if (tagName.equals("set")) {
1486             parser.next();
1487             res = readThisSetXml(parser, "set", name, callback, arrayMap);
1488             name[0] = valueName;
1489             //System.out.println("Returning value for " + valueName + ": " + res);
1490             return res;
1491         } else if (callback != null) {
1492             res = callback.readThisUnknownObjectXml(parser, tagName);
1493             name[0] = valueName;
1494             return res;
1495         } else {
1496             throw new XmlPullParserException("Unknown tag: " + tagName);
1497         }
1498 
1499         // Skip through to end tag.
1500         int eventType;
1501         while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1502             if (eventType == parser.END_TAG) {
1503                 if (parser.getName().equals(tagName)) {
1504                     name[0] = valueName;
1505                     //System.out.println("Returning value for " + valueName + ": " + res);
1506                     return res;
1507                 }
1508                 throw new XmlPullParserException(
1509                     "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1510             } else if (eventType == parser.TEXT) {
1511                 throw new XmlPullParserException(
1512                 "Unexpected text in <" + tagName + ">: " + parser.getName());
1513             } else if (eventType == parser.START_TAG) {
1514                 throw new XmlPullParserException(
1515                     "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1516             }
1517         }
1518         throw new XmlPullParserException(
1519             "Unexpected end of document in <" + tagName + ">");
1520     }
1521 
readThisPrimitiveValueXml(XmlPullParser parser, String tagName)1522     private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
1523     throws XmlPullParserException, java.io.IOException
1524     {
1525         try {
1526             if (tagName.equals("int")) {
1527                 return Integer.parseInt(parser.getAttributeValue(null, "value"));
1528             } else if (tagName.equals("long")) {
1529                 return Long.valueOf(parser.getAttributeValue(null, "value"));
1530             } else if (tagName.equals("float")) {
1531                 return new Float(parser.getAttributeValue(null, "value"));
1532             } else if (tagName.equals("double")) {
1533                 return new Double(parser.getAttributeValue(null, "value"));
1534             } else if (tagName.equals("boolean")) {
1535                 return Boolean.valueOf(parser.getAttributeValue(null, "value"));
1536             } else {
1537                 return null;
1538             }
1539         } catch (NullPointerException e) {
1540             throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
1541         } catch (NumberFormatException e) {
1542             throw new XmlPullParserException(
1543                     "Not a number in value attribute in <" + tagName + ">");
1544         }
1545     }
1546 
1547     @UnsupportedAppUsage
beginDocument(XmlPullParser parser, String firstElementName)1548     public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1549     {
1550         int type;
1551         while ((type=parser.next()) != parser.START_TAG
1552                    && type != parser.END_DOCUMENT) {
1553             ;
1554         }
1555 
1556         if (type != parser.START_TAG) {
1557             throw new XmlPullParserException("No start tag found");
1558         }
1559 
1560         if (!parser.getName().equals(firstElementName)) {
1561             throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1562                     ", expected " + firstElementName);
1563         }
1564     }
1565 
1566     @UnsupportedAppUsage
nextElement(XmlPullParser parser)1567     public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1568     {
1569         int type;
1570         while ((type=parser.next()) != parser.START_TAG
1571                    && type != parser.END_DOCUMENT) {
1572             ;
1573         }
1574     }
1575 
nextElementWithin(XmlPullParser parser, int outerDepth)1576     public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1577             throws IOException, XmlPullParserException {
1578         for (;;) {
1579             int type = parser.next();
1580             if (type == XmlPullParser.END_DOCUMENT
1581                     || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1582                 return false;
1583             }
1584             if (type == XmlPullParser.START_TAG
1585                     && parser.getDepth() == outerDepth + 1) {
1586                 return true;
1587             }
1588         }
1589     }
1590 
readIntAttribute(XmlPullParser in, String name, int defaultValue)1591     public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1592         final String value = in.getAttributeValue(null, name);
1593         if (TextUtils.isEmpty(value)) {
1594             return defaultValue;
1595         }
1596         try {
1597             return Integer.parseInt(value);
1598         } catch (NumberFormatException e) {
1599             return defaultValue;
1600         }
1601     }
1602 
readIntAttribute(XmlPullParser in, String name)1603     public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1604         final String value = in.getAttributeValue(null, name);
1605         try {
1606             return Integer.parseInt(value);
1607         } catch (NumberFormatException e) {
1608             throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1609         }
1610     }
1611 
writeIntAttribute(XmlSerializer out, String name, int value)1612     public static void writeIntAttribute(XmlSerializer out, String name, int value)
1613             throws IOException {
1614         out.attribute(null, name, Integer.toString(value));
1615     }
1616 
readLongAttribute(XmlPullParser in, String name, long defaultValue)1617     public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1618         final String value = in.getAttributeValue(null, name);
1619         if (TextUtils.isEmpty(value)) {
1620             return defaultValue;
1621         }
1622         try {
1623             return Long.parseLong(value);
1624         } catch (NumberFormatException e) {
1625             return defaultValue;
1626         }
1627     }
1628 
readLongAttribute(XmlPullParser in, String name)1629     public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1630         final String value = in.getAttributeValue(null, name);
1631         try {
1632             return Long.parseLong(value);
1633         } catch (NumberFormatException e) {
1634             throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1635         }
1636     }
1637 
writeLongAttribute(XmlSerializer out, String name, long value)1638     public static void writeLongAttribute(XmlSerializer out, String name, long value)
1639             throws IOException {
1640         out.attribute(null, name, Long.toString(value));
1641     }
1642 
readFloatAttribute(XmlPullParser in, String name)1643     public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
1644         final String value = in.getAttributeValue(null, name);
1645         try {
1646             return Float.parseFloat(value);
1647         } catch (NumberFormatException e) {
1648             throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1649         }
1650     }
1651 
writeFloatAttribute(XmlSerializer out, String name, float value)1652     public static void writeFloatAttribute(XmlSerializer out, String name, float value)
1653             throws IOException {
1654         out.attribute(null, name, Float.toString(value));
1655     }
1656 
readBooleanAttribute(XmlPullParser in, String name)1657     public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1658         final String value = in.getAttributeValue(null, name);
1659         return Boolean.parseBoolean(value);
1660     }
1661 
readBooleanAttribute(XmlPullParser in, String name, boolean defaultValue)1662     public static boolean readBooleanAttribute(XmlPullParser in, String name,
1663             boolean defaultValue) {
1664         final String value = in.getAttributeValue(null, name);
1665         if (TextUtils.isEmpty(value)) {
1666             return defaultValue;
1667         } else {
1668             return Boolean.parseBoolean(value);
1669         }
1670     }
1671 
writeBooleanAttribute(XmlSerializer out, String name, boolean value)1672     public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1673             throws IOException {
1674         out.attribute(null, name, Boolean.toString(value));
1675     }
1676 
readUriAttribute(XmlPullParser in, String name)1677     public static Uri readUriAttribute(XmlPullParser in, String name) {
1678         final String value = in.getAttributeValue(null, name);
1679         return (value != null) ? Uri.parse(value) : null;
1680     }
1681 
writeUriAttribute(XmlSerializer out, String name, Uri value)1682     public static void writeUriAttribute(XmlSerializer out, String name, Uri value)
1683             throws IOException {
1684         if (value != null) {
1685             out.attribute(null, name, value.toString());
1686         }
1687     }
1688 
readStringAttribute(XmlPullParser in, String name)1689     public static String readStringAttribute(XmlPullParser in, String name) {
1690         return in.getAttributeValue(null, name);
1691     }
1692 
writeStringAttribute(XmlSerializer out, String name, CharSequence value)1693     public static void writeStringAttribute(XmlSerializer out, String name, CharSequence value)
1694             throws IOException {
1695         if (value != null) {
1696             out.attribute(null, name, value.toString());
1697         }
1698     }
1699 
readByteArrayAttribute(XmlPullParser in, String name)1700     public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
1701         final String value = in.getAttributeValue(null, name);
1702         if (!TextUtils.isEmpty(value)) {
1703             return Base64.decode(value, Base64.DEFAULT);
1704         } else {
1705             return null;
1706         }
1707     }
1708 
writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)1709     public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
1710             throws IOException {
1711         if (value != null) {
1712             out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT));
1713         }
1714     }
1715 
readBitmapAttribute(XmlPullParser in, String name)1716     public static Bitmap readBitmapAttribute(XmlPullParser in, String name) {
1717         final byte[] value = readByteArrayAttribute(in, name);
1718         if (value != null) {
1719             return BitmapFactory.decodeByteArray(value, 0, value.length);
1720         } else {
1721             return null;
1722         }
1723     }
1724 
1725     @Deprecated
writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)1726     public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
1727             throws IOException {
1728         if (value != null) {
1729             final ByteArrayOutputStream os = new ByteArrayOutputStream();
1730             value.compress(CompressFormat.PNG, 90, os);
1731             writeByteArrayAttribute(out, name, os.toByteArray());
1732         }
1733     }
1734 
1735     /** @hide */
1736     public interface WriteMapCallback {
1737         /**
1738          * Called from writeMapXml when an Object type is not recognized. The implementer
1739          * must write out the entire element including start and end tags.
1740          *
1741          * @param v The object to be written out
1742          * @param name The mapping key for v. Must be written into the "name" attribute of the
1743          *             start tag.
1744          * @param out The XML output stream.
1745          * @throws XmlPullParserException on unrecognized Object type.
1746          * @throws IOException on XmlSerializer serialization errors.
1747          * @hide
1748          */
writeUnknownObject(Object v, String name, XmlSerializer out)1749          public void writeUnknownObject(Object v, String name, XmlSerializer out)
1750                  throws XmlPullParserException, IOException;
1751     }
1752 
1753     /** @hide */
1754     public interface ReadMapCallback {
1755         /**
1756          * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1757          * is positioned within the start tag so that attributes can be read using in.getAttribute.
1758          *
1759          * @param in the XML input stream
1760          * @param tag the START_TAG that was not recognized.
1761          * @return the Object parsed from the stream which will be put into the map.
1762          * @throws XmlPullParserException if the START_TAG is not recognized.
1763          * @throws IOException on XmlPullParser serialization errors.
1764          * @hide
1765          */
readThisUnknownObjectXml(XmlPullParser in, String tag)1766         public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
1767                 throws XmlPullParserException, IOException;
1768     }
1769 }
1770