Home | History | Annotate | Download | only in util
      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 
     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
     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
     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
     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 
    136     public static int convertValueToUnsignedInt(String value, int defaultValue) {
    137         if (null == value) {
    138             return defaultValue;
    139         }
    140 
    141         return parseUnsignedIntAttribute(value);
    142     }
    143 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    640     private static final void writeValueXml(Object v, String name, XmlSerializer out,
    641             WriteMapCallback callback)  throws XmlPullParserException, java.io.IOException {
    642         String typeStr;
    643         if (v == null) {
    644             out.startTag(null, "null");
    645             if (name != null) {
    646                 out.attribute(null, "name", name);
    647             }
    648             out.endTag(null, "null");
    649             return;
    650         } else if (v instanceof String) {
    651             out.startTag(null, "string");
    652             if (name != null) {
    653                 out.attribute(null, "name", name);
    654             }
    655             out.text(v.toString());
    656             out.endTag(null, "string");
    657             return;
    658         } else if (v instanceof Integer) {
    659             typeStr = "int";
    660         } else if (v instanceof Long) {
    661             typeStr = "long";
    662         } else if (v instanceof Float) {
    663             typeStr = "float";
    664         } else if (v instanceof Double) {
    665             typeStr = "double";
    666         } else if (v instanceof Boolean) {
    667             typeStr = "boolean";
    668         } else if (v instanceof byte[]) {
    669             writeByteArrayXml((byte[])v, name, out);
    670             return;
    671         } else if (v instanceof int[]) {
    672             writeIntArrayXml((int[])v, name, out);
    673             return;
    674         } else if (v instanceof long[]) {
    675             writeLongArrayXml((long[])v, name, out);
    676             return;
    677         } else if (v instanceof double[]) {
    678             writeDoubleArrayXml((double[])v, name, out);
    679             return;
    680         } else if (v instanceof String[]) {
    681             writeStringArrayXml((String[])v, name, out);
    682             return;
    683         } else if (v instanceof boolean[]) {
    684             writeBooleanArrayXml((boolean[])v, name, out);
    685             return;
    686         } else if (v instanceof Map) {
    687             writeMapXml((Map)v, name, out);
    688             return;
    689         } else if (v instanceof List) {
    690             writeListXml((List) v, name, out);
    691             return;
    692         } else if (v instanceof Set) {
    693             writeSetXml((Set) v, name, out);
    694             return;
    695         } else if (v instanceof CharSequence) {
    696             // XXX This is to allow us to at least write something if
    697             // we encounter styled text...  but it means we will drop all
    698             // of the styling information. :(
    699             out.startTag(null, "string");
    700             if (name != null) {
    701                 out.attribute(null, "name", name);
    702             }
    703             out.text(v.toString());
    704             out.endTag(null, "string");
    705             return;
    706         } else if (callback != null) {
    707             callback.writeUnknownObject(v, name, out);
    708             return;
    709         } else {
    710             throw new RuntimeException("writeValueXml: unable to write value " + v);
    711         }
    712 
    713         out.startTag(null, typeStr);
    714         if (name != null) {
    715             out.attribute(null, "name", name);
    716         }
    717         out.attribute(null, "value", v.toString());
    718         out.endTag(null, typeStr);
    719     }
    720 
    721     /**
    722      * Read a HashMap from an InputStream containing XML.  The stream can
    723      * previously have been written by writeMapXml().
    724      *
    725      * @param in The InputStream from which to read.
    726      *
    727      * @return HashMap The resulting map.
    728      *
    729      * @see #readListXml
    730      * @see #readValueXml
    731      * @see #readThisMapXml
    732      * #see #writeMapXml
    733      */
    734     @SuppressWarnings("unchecked")
    735     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   1616     public static void writeIntAttribute(XmlSerializer out, String name, int value)
   1617             throws IOException {
   1618         out.attribute(null, name, Integer.toString(value));
   1619     }
   1620 
   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 
   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 
   1642     public static void writeLongAttribute(XmlSerializer out, String name, long value)
   1643             throws IOException {
   1644         out.attribute(null, name, Long.toString(value));
   1645     }
   1646 
   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 
   1656     public static void writeFloatAttribute(XmlSerializer out, String name, float value)
   1657             throws IOException {
   1658         out.attribute(null, name, Float.toString(value));
   1659     }
   1660 
   1661     public static boolean readBooleanAttribute(XmlPullParser in, String name) {
   1662         final String value = in.getAttributeValue(null, name);
   1663         return Boolean.parseBoolean(value);
   1664     }
   1665 
   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 
   1676     public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
   1677             throws IOException {
   1678         out.attribute(null, name, Boolean.toString(value));
   1679     }
   1680 
   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 
   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 
   1693     public static String readStringAttribute(XmlPullParser in, String name) {
   1694         return in.getAttributeValue(null, name);
   1695     }
   1696 
   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 
   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 
   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 
   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
   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          */
   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          */
   1770         public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
   1771                 throws XmlPullParserException, IOException;
   1772     }
   1773 }
   1774