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