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