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