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