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