Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright (C) 2016 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.voicemail.impl.utils;
     18 
     19 import android.util.ArrayMap;
     20 import java.io.IOException;
     21 import java.util.ArrayList;
     22 import java.util.List;
     23 import org.xmlpull.v1.XmlPullParser;
     24 import org.xmlpull.v1.XmlPullParserException;
     25 
     26 public class XmlUtils {
     27 
     28   public static final ArrayMap<String, ?> readThisArrayMapXml(
     29       XmlPullParser parser, String endTag, String[] name, ReadMapCallback callback)
     30       throws XmlPullParserException, java.io.IOException {
     31     ArrayMap<String, Object> map = new ArrayMap<>();
     32 
     33     int eventType = parser.getEventType();
     34     do {
     35       if (eventType == XmlPullParser.START_TAG) {
     36         Object val = readThisValueXml(parser, name, callback, true);
     37         map.put(name[0], val);
     38       } else if (eventType == XmlPullParser.END_TAG) {
     39         if (parser.getName().equals(endTag)) {
     40           return map;
     41         }
     42         throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName());
     43       }
     44       eventType = parser.next();
     45     } while (eventType != XmlPullParser.END_DOCUMENT);
     46 
     47     throw new XmlPullParserException("Document ended before " + endTag + " end tag");
     48   }
     49 
     50   /**
     51    * Read an ArrayList object from an XmlPullParser. The XML data could previously have been
     52    * generated by writeListXml(). The XmlPullParser must be positioned <em>after</em> the tag that
     53    * begins the list.
     54    *
     55    * @param parser The XmlPullParser from which to read the list data.
     56    * @param endTag Name of the tag that will end the list, usually "list".
     57    * @param name An array of one string, used to return the name attribute of the list's tag.
     58    * @return HashMap The newly generated list.
     59    */
     60   public static final ArrayList readThisListXml(
     61       XmlPullParser parser,
     62       String endTag,
     63       String[] name,
     64       ReadMapCallback callback,
     65       boolean arrayMap)
     66       throws XmlPullParserException, java.io.IOException {
     67     ArrayList list = new ArrayList();
     68 
     69     int eventType = parser.getEventType();
     70     do {
     71       if (eventType == XmlPullParser.START_TAG) {
     72         Object val = readThisValueXml(parser, name, callback, arrayMap);
     73         list.add(val);
     74       } else if (eventType == XmlPullParser.END_TAG) {
     75         if (parser.getName().equals(endTag)) {
     76           return list;
     77         }
     78         throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName());
     79       }
     80       eventType = parser.next();
     81     } while (eventType != XmlPullParser.END_DOCUMENT);
     82 
     83     throw new XmlPullParserException("Document ended before " + endTag + " end tag");
     84   }
     85 
     86   /**
     87    * Read a String[] object from an XmlPullParser. The XML data could previously have been generated
     88    * by writeStringArrayXml(). The XmlPullParser must be positioned <em>after</em> the tag that
     89    * begins the list.
     90    *
     91    * @param parser The XmlPullParser from which to read the list data.
     92    * @param endTag Name of the tag that will end the list, usually "string-array".
     93    * @param name An array of one string, used to return the name attribute of the list's tag.
     94    * @return Returns a newly generated String[].
     95    */
     96   public static String[] readThisStringArrayXml(XmlPullParser parser, String endTag, String[] name)
     97       throws XmlPullParserException, java.io.IOException {
     98 
     99     parser.next();
    100 
    101     List<String> array = new ArrayList<>();
    102 
    103     int eventType = parser.getEventType();
    104     do {
    105       if (eventType == XmlPullParser.START_TAG) {
    106         if (parser.getName().equals("item")) {
    107           try {
    108             array.add(parser.getAttributeValue(null, "value"));
    109           } catch (NullPointerException e) {
    110             throw new XmlPullParserException("Need value attribute in item");
    111           } catch (NumberFormatException e) {
    112             throw new XmlPullParserException("Not a number in value attribute in item");
    113           }
    114         } else {
    115           throw new XmlPullParserException("Expected item tag at: " + parser.getName());
    116         }
    117       } else if (eventType == XmlPullParser.END_TAG) {
    118         if (parser.getName().equals(endTag)) {
    119           return array.toArray(new String[0]);
    120         } else if (parser.getName().equals("item")) {
    121 
    122         } else {
    123           throw new XmlPullParserException(
    124               "Expected " + endTag + " end tag at: " + parser.getName());
    125         }
    126       }
    127       eventType = parser.next();
    128     } while (eventType != XmlPullParser.END_DOCUMENT);
    129 
    130     throw new XmlPullParserException("Document ended before " + endTag + " end tag");
    131   }
    132 
    133   private static Object readThisValueXml(
    134       XmlPullParser parser, String[] name, ReadMapCallback callback, boolean arrayMap)
    135       throws XmlPullParserException, java.io.IOException {
    136     final String valueName = parser.getAttributeValue(null, "name");
    137     final String tagName = parser.getName();
    138 
    139     Object res;
    140 
    141     if (tagName.equals("null")) {
    142       res = null;
    143     } else if (tagName.equals("string")) {
    144       String value = "";
    145       int eventType;
    146       while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
    147         if (eventType == XmlPullParser.END_TAG) {
    148           if (parser.getName().equals("string")) {
    149             name[0] = valueName;
    150             return value;
    151           }
    152           throw new XmlPullParserException("Unexpected end tag in <string>: " + parser.getName());
    153         } else if (eventType == XmlPullParser.TEXT) {
    154           value += parser.getText();
    155         } else if (eventType == XmlPullParser.START_TAG) {
    156           throw new XmlPullParserException("Unexpected start tag in <string>: " + parser.getName());
    157         }
    158       }
    159       throw new XmlPullParserException("Unexpected end of document in <string>");
    160     } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
    161       // all work already done by readThisPrimitiveValueXml
    162     } else if (tagName.equals("string-array")) {
    163       res = readThisStringArrayXml(parser, "string-array", name);
    164       name[0] = valueName;
    165       return res;
    166     } else if (tagName.equals("list")) {
    167       parser.next();
    168       res = readThisListXml(parser, "list", name, callback, arrayMap);
    169       name[0] = valueName;
    170       return res;
    171     } else if (callback != null) {
    172       res = callback.readThisUnknownObjectXml(parser, tagName);
    173       name[0] = valueName;
    174       return res;
    175     } else {
    176       throw new XmlPullParserException("Unknown tag: " + tagName);
    177     }
    178 
    179     // Skip through to end tag.
    180     int eventType;
    181     while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
    182       if (eventType == XmlPullParser.END_TAG) {
    183         if (parser.getName().equals(tagName)) {
    184           name[0] = valueName;
    185           return res;
    186         }
    187         throw new XmlPullParserException(
    188             "Unexpected end tag in <" + tagName + ">: " + parser.getName());
    189       } else if (eventType == XmlPullParser.TEXT) {
    190         throw new XmlPullParserException(
    191             "Unexpected text in <" + tagName + ">: " + parser.getName());
    192       } else if (eventType == XmlPullParser.START_TAG) {
    193         throw new XmlPullParserException(
    194             "Unexpected start tag in <" + tagName + ">: " + parser.getName());
    195       }
    196     }
    197     throw new XmlPullParserException("Unexpected end of document in <" + tagName + ">");
    198   }
    199 
    200   private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
    201       throws XmlPullParserException, java.io.IOException {
    202     try {
    203       if (tagName.equals("int")) {
    204         return Integer.parseInt(parser.getAttributeValue(null, "value"));
    205       } else if (tagName.equals("long")) {
    206         return Long.valueOf(parser.getAttributeValue(null, "value"));
    207       } else if (tagName.equals("float")) {
    208         return Float.valueOf(parser.getAttributeValue(null, "value"));
    209       } else if (tagName.equals("double")) {
    210         return Double.valueOf(parser.getAttributeValue(null, "value"));
    211       } else if (tagName.equals("boolean")) {
    212         return Boolean.valueOf(parser.getAttributeValue(null, "value"));
    213       } else {
    214         return null;
    215       }
    216     } catch (NullPointerException e) {
    217       throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
    218     } catch (NumberFormatException e) {
    219       throw new XmlPullParserException("Not a number in value attribute in <" + tagName + ">");
    220     }
    221   }
    222 
    223   public interface ReadMapCallback {
    224 
    225     /**
    226      * Called from readThisMapXml when a START_TAG is not recognized. The input stream is positioned
    227      * within the start tag so that attributes can be read using in.getAttribute.
    228      *
    229      * @param in the XML input stream
    230      * @param tag the START_TAG that was not recognized.
    231      * @return the Object parsed from the stream which will be put into the map.
    232      * @throws XmlPullParserException if the START_TAG is not recognized.
    233      * @throws IOException on XmlPullParser serialization errors.
    234      */
    235     Object readThisUnknownObjectXml(XmlPullParser in, String tag)
    236         throws XmlPullParserException, IOException;
    237   }
    238 }
    239