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