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