1 package com.xtremelabs.robolectric.shadows; 2 3 import static com.xtremelabs.robolectric.Robolectric.shadowOf; 4 5 import android.os.Bundle; 6 import android.os.Parcel; 7 import android.os.Parcelable; 8 import android.text.TextUtils; 9 import android.util.Log; 10 import android.util.Pair; 11 12 import com.xtremelabs.robolectric.Robolectric; 13 import com.xtremelabs.robolectric.bytecode.ShadowWrangler; 14 import com.xtremelabs.robolectric.internal.Implementation; 15 import com.xtremelabs.robolectric.internal.Implements; 16 import com.xtremelabs.robolectric.internal.RealObject; 17 18 import java.lang.reflect.Field; 19 import java.util.ArrayList; 20 import java.util.HashMap; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.Set; 24 25 @Implements(Parcel.class) 26 @SuppressWarnings("unchecked") 27 public class ShadowParcel { 28 29 private static final int VAL_NULL = -1; 30 private static final int VAL_STRING = 0; 31 private static final int VAL_INTEGER = 1; 32 private static final int VAL_MAP = 2; 33 private static final int VAL_BUNDLE = 3; 34 private static final int VAL_PARCELABLE = 4; 35 private static final int VAL_SHORT = 5; 36 private static final int VAL_LONG = 6; 37 private static final int VAL_FLOAT = 7; 38 private static final int VAL_DOUBLE = 8; 39 private static final int VAL_BOOLEAN = 9; 40 private static final int VAL_CHARSEQUENCE = 10; 41 private static final int VAL_BYTEARRAY = 13; 42 private static final int VAL_STRINGARRAY = 14; 43 private static final int VAL_OBJECTARRAY = 17; 44 private static final int VAL_INTARRAY = 18; 45 private static final int VAL_LONGARRAY = 19; 46 private static final int VAL_BYTE = 20; 47 private static final int VAL_BOOLEANARRAY = 23; 48 49 private final ArrayList<Pair<Integer, ?>> parcelData = new ArrayList<Pair<Integer, ?>>(); 50 private int index = 0; 51 52 @RealObject 53 private Parcel realParcel; 54 55 @Implementation 56 public static Parcel obtain() { 57 return Robolectric.newInstanceOf(Parcel.class); 58 } 59 60 @Implementation 61 public int dataAvail() { 62 return dataSize() - dataPosition(); 63 } 64 65 @Implementation 66 public int dataPosition() { 67 return calculateSizeToIndex(index); 68 } 69 70 @Implementation 71 public int dataSize() { 72 return calculateSizeToIndex(parcelData.size()); 73 } 74 75 @Implementation 76 public int dataCapacity() { 77 return dataSize(); 78 } 79 80 @Implementation 81 public void setDataPosition(int pos) { 82 index = calculateIndexFromSizePosition(pos); 83 } 84 85 private int calculateSizeToIndex(int index) { 86 int size = 0; 87 for (int i = 0; i < index; i++) { 88 size += parcelData.get(i).first; 89 } 90 return size; 91 } 92 93 private int calculateIndexFromSizePosition(int pos) { 94 int size = 0; 95 for (int i = 0; i < parcelData.size(); i++) { 96 if (size >= pos) { 97 return i; 98 } 99 size += parcelData.get(i).first; 100 } 101 return parcelData.size(); 102 } 103 104 @Implementation 105 public void writeString(String str) { 106 if (str == null) { 107 writeInt(-1); 108 } else { 109 writeInt(str.length()); 110 addValueToList(Pair.create(str.length(), str)); 111 } 112 } 113 114 @Implementation 115 public String readString() { 116 int N = readInt(); 117 if (N < 0) { 118 return null; 119 } else { 120 return readValueFromList(null); 121 } 122 } 123 124 @Implementation 125 public void writeInt(int i) { 126 addValueToList(Pair.create(Integer.SIZE / 8, i)); 127 } 128 129 @Implementation 130 public int readInt() { 131 return readValueFromList(0); 132 } 133 134 @Implementation 135 public void writeLong(long i) { 136 addValueToList(Pair.create(Long.SIZE / 8, i)); 137 } 138 139 @Implementation 140 public long readLong() { 141 return readValueFromList((long) 0); 142 } 143 144 @Implementation 145 public void writeFloat(float f) { 146 addValueToList(Pair.create(Float.SIZE / 8, f)); 147 } 148 149 @Implementation 150 public float readFloat() { 151 return readValueFromList((float) 0); 152 } 153 154 @Implementation 155 public void writeDouble(double f) { 156 addValueToList(Pair.create(Double.SIZE / 8, f)); 157 } 158 159 @Implementation 160 public double readDouble() { 161 return readValueFromList((double) 0); 162 } 163 164 public void writeBoolean(boolean b) { 165 addValueToList(Pair.create(1, b)); 166 } 167 168 public boolean readBoolean() { 169 return readValueFromList(false); 170 } 171 172 public void writeChar(char c) { 173 addValueToList(Pair.create(Character.SIZE / 8, c)); 174 } 175 176 public char readChar() { 177 return readValueFromList((char) 0); 178 } 179 180 @Implementation 181 @SuppressWarnings("unchecked") 182 public void writeByte(byte b) { 183 addValueToList(Pair.create(Byte.SIZE / 8, b)); 184 } 185 186 @Implementation 187 public byte readByte() { 188 return readValueFromList((byte) 0); 189 } 190 191 @Implementation 192 public void readBooleanArray(boolean[] val) { 193 int N = readInt(); 194 if (val.length != N) 195 throw new RuntimeException("bad array lengths"); 196 for (int i = 0; i < val.length; i++) { 197 val[i] = readBoolean(); 198 } 199 } 200 201 @Implementation 202 public void writeBooleanArray(boolean[] val) { 203 if (val == null) { 204 writeInt(-1); 205 return; 206 } 207 writeInt(val.length); 208 for (boolean b : val) 209 writeBoolean(b); 210 } 211 212 @Implementation 213 public boolean[] createBooleanArray() { 214 int N = readInt(); 215 if (N < 0) { 216 return null; 217 } 218 boolean[] val = new boolean[N]; 219 for (int i = 0; i < val.length; i++) { 220 val[i] = readBoolean(); 221 } 222 return val; 223 } 224 225 @Implementation 226 public void readCharArray(char[] val) { 227 int N = readInt(); 228 if (val.length != N) 229 throw new RuntimeException("bad array lengths"); 230 for (int i = 0; i < val.length; i++) { 231 val[i] = readChar(); 232 } 233 } 234 235 @Implementation 236 public void writeCharArray(char[] val) { 237 if (val == null) { 238 writeInt(-1); 239 return; 240 } 241 writeInt(val.length); 242 for (char b : val) 243 writeChar(b); 244 } 245 246 @Implementation 247 public char[] createCharArray() { 248 int N = readInt(); 249 if (N < 0) { 250 return null; 251 } 252 char[] val = new char[N]; 253 for (int i = 0; i < val.length; i++) { 254 val[i] = readChar(); 255 } 256 return val; 257 } 258 259 @Implementation 260 public void readFloatArray(float[] val) { 261 int N = readInt(); 262 if (val.length != N) 263 throw new RuntimeException("bad array lengths"); 264 for (int i = 0; i < val.length; i++) { 265 val[i] = readFloat(); 266 } 267 } 268 269 @Implementation 270 public void writeFloatArray(float[] val) { 271 if (val == null) { 272 writeInt(-1); 273 return; 274 } 275 writeInt(val.length); 276 for (float f : val) 277 writeFloat(f); 278 } 279 280 @Implementation 281 public float[] createFloatArray() { 282 int N = readInt(); 283 if (N < 0) { 284 return null; 285 } 286 float[] val = new float[N]; 287 for (int i = 0; i < val.length; i++) { 288 val[i] = readFloat(); 289 } 290 return val; 291 } 292 293 @Implementation 294 public void writeDoubleArray(double[] val) { 295 if (val == null) { 296 writeInt(-1); 297 return; 298 } 299 writeInt(val.length); 300 for (double f : val) 301 writeDouble(f); 302 } 303 304 @Implementation 305 public void readDoubleArray(double[] val) { 306 int N = readInt(); 307 if (val.length != N) 308 throw new RuntimeException("bad array lengths"); 309 for (int i = 0; i < val.length; i++) { 310 val[i] = readDouble(); 311 } 312 } 313 314 @Implementation 315 public double[] createDoubleArray() { 316 int N = readInt(); 317 if (N < 0) { 318 return null; 319 } 320 double[] val = new double[N]; 321 for (int i = 0; i < val.length; i++) { 322 val[i] = readDouble(); 323 } 324 return val; 325 } 326 327 @Implementation 328 public void writeIntArray(int[] val) { 329 if (val == null) { 330 writeInt(-1); 331 return; 332 } 333 writeInt(val.length); 334 for (int f : val) 335 writeInt(f); 336 } 337 338 @Implementation 339 public void readIntArray(int[] val) { 340 int N = readInt(); 341 if (val.length != N) 342 throw new RuntimeException("bad array lengths"); 343 for (int i = 0; i < val.length; i++) { 344 val[i] = readInt(); 345 } 346 } 347 348 @Implementation 349 public int[] createIntArray() { 350 int N = readInt(); 351 if (N < 0) { 352 return null; 353 } 354 int[] val = new int[N]; 355 for (int i = 0; i < val.length; i++) { 356 val[i] = readInt(); 357 } 358 return val; 359 } 360 361 @Implementation 362 public void writeByteArray(byte[] val) { 363 if (val == null) { 364 writeInt(-1); 365 return; 366 } 367 writeInt(val.length); 368 for (byte f : val) 369 writeByte(f); 370 } 371 372 @Implementation 373 public void readByteArray(byte[] val) { 374 int N = readInt(); 375 if (val.length != N) 376 throw new RuntimeException("bad array lengths"); 377 for (int i = 0; i < val.length; i++) { 378 val[i] = readByte(); 379 } 380 } 381 382 @Implementation 383 public byte[] createByteArray() { 384 int N = readInt(); 385 if (N < 0) { 386 return null; 387 } 388 byte[] val = new byte[N]; 389 for (int i = 0; i < val.length; i++) { 390 val[i] = readByte(); 391 } 392 return val; 393 } 394 395 @Implementation 396 public void writeLongArray(long[] val) { 397 if (val == null) { 398 writeInt(-1); 399 return; 400 } 401 writeInt(val.length); 402 for (long f : val) 403 writeLong(f); 404 } 405 406 @Implementation 407 public void readLongArray(long[] val) { 408 int N = readInt(); 409 if (val.length != N) 410 throw new RuntimeException("bad array lengths"); 411 for (int i = 0; i < val.length; i++) { 412 val[i] = readLong(); 413 } 414 } 415 416 @Implementation 417 public long[] createLongArray() { 418 int N = readInt(); 419 if (N < 0) { 420 return null; 421 } 422 long[] val = new long[N]; 423 for (int i = 0; i < val.length; i++) { 424 val[i] = readLong(); 425 } 426 return val; 427 } 428 429 @Implementation 430 public void writeStringArray(String[] val) { 431 if (val == null) { 432 writeInt(-1); 433 return; 434 } 435 writeInt(val.length); 436 for (String f : val) 437 writeString(f); 438 } 439 440 @Implementation 441 public String[] createStringArray() { 442 String[] array = null; 443 444 int N = readInt(); 445 if (N >= 0) { 446 array = new String[N]; 447 for (int i = 0; i < N; i++) { 448 array[i] = readString(); 449 } 450 } 451 return array; 452 } 453 454 @Implementation 455 public void readStringArray(String[] dest) { 456 int N = readInt(); 457 if (dest.length != N) 458 throw new RuntimeException("bad array lengths"); 459 for (int i = 0; i < dest.length; i++) { 460 dest[i] = readString(); 461 } 462 } 463 464 @Implementation 465 public void writeStringList(List<String> strings) { 466 if (strings == null) { 467 writeInt(-1); 468 return; 469 } 470 int count = strings.size(); 471 int i = 0; 472 writeInt(count); 473 while (i < count) { 474 writeString(strings.get(i)); 475 i++; 476 } 477 } 478 479 @Implementation 480 public void readStringList(List<String> list) { 481 int listSizeBeforeChange = list.size(); 482 int addCount = readInt(); 483 int i = 0; 484 for (; i < listSizeBeforeChange && i < addCount; i++) { 485 list.set(i, readString()); 486 } 487 for (; i < addCount; i++) { 488 list.add(readString()); 489 } 490 for (; i < listSizeBeforeChange; i++) { 491 list.remove(addCount); 492 } 493 } 494 495 @Implementation 496 public ArrayList<String> createStringArrayList() { 497 int N = readInt(); 498 if (N < 0) { 499 return null; 500 } 501 502 ArrayList<String> l = new ArrayList<String>(N); 503 while (N > 0) { 504 l.add(readString()); 505 N--; 506 } 507 return l; 508 } 509 510 @Implementation 511 public void writeArray(Object[] values) { 512 if (values == null) { 513 writeInt(-1); 514 return; 515 } 516 int N = values.length; 517 writeInt(N); 518 for (Object value : values) { 519 writeValue(value); 520 } 521 } 522 523 @Implementation 524 public Object[] readArray(ClassLoader loader) { 525 int N = readInt(); 526 if (N < 0) { 527 return null; 528 } 529 Object[] l = new Object[N]; 530 readArrayInternal(l, N, loader); 531 return l; 532 } 533 534 @Implementation 535 public void writeValue(Object v) { 536 if (v == null) { 537 writeInt(VAL_NULL); 538 } else if (v instanceof String) { 539 writeInt(VAL_STRING); 540 writeString((String) v); 541 } else if (v instanceof Integer) { 542 writeInt(VAL_INTEGER); 543 writeInt((Integer) v); 544 } else if (v instanceof Map) { 545 writeInt(VAL_MAP); 546 writeMap((Map) v); 547 } else if (v instanceof Bundle) { 548 // Must be before Parcelable 549 writeInt(VAL_BUNDLE); 550 writeBundle((Bundle) v); 551 } else if (v instanceof Parcelable) { 552 writeInt(VAL_PARCELABLE); 553 writeParcelable((Parcelable) v, 0); 554 } else if (v instanceof Short) { 555 writeInt(VAL_SHORT); 556 writeInt(((Short) v).intValue()); 557 } else if (v instanceof Long) { 558 writeInt(VAL_LONG); 559 writeLong((Long) v); 560 } else if (v instanceof Float) { 561 writeInt(VAL_FLOAT); 562 writeFloat((Float) v); 563 } else if (v instanceof Double) { 564 writeInt(VAL_DOUBLE); 565 writeDouble((Double) v); 566 } else if (v instanceof Boolean) { 567 writeInt(VAL_BOOLEAN); 568 writeInt((Boolean) v ? 1 : 0); 569 } else if (v instanceof CharSequence) { 570 // Must be after String 571 writeInt(VAL_CHARSEQUENCE); 572 TextUtils.writeToParcel((CharSequence) v, realParcel, 0); 573 } else if (v instanceof boolean[]) { 574 writeInt(VAL_BOOLEANARRAY); 575 writeBooleanArray((boolean[]) v); 576 } else if (v instanceof byte[]) { 577 writeInt(VAL_BYTEARRAY); 578 writeByteArray((byte[]) v); 579 } else if (v instanceof String[]) { 580 writeInt(VAL_STRINGARRAY); 581 writeStringArray((String[]) v); 582 } else if (v instanceof Object[]) { 583 writeInt(VAL_OBJECTARRAY); 584 writeArray((Object[]) v); 585 } else if (v instanceof int[]) { 586 writeInt(VAL_INTARRAY); 587 writeIntArray((int[]) v); 588 } else if (v instanceof long[]) { 589 writeInt(VAL_LONGARRAY); 590 writeLongArray((long[]) v); 591 } else if (v instanceof Byte) { 592 writeInt(VAL_BYTE); 593 writeByte((Byte) v); 594 } else { 595 throw new RuntimeException( 596 "Parcel: unable to marshal value with type" + v.getClass().getName()); 597 } 598 } 599 600 @Implementation 601 public Object readValue(ClassLoader loader) { 602 int type = readInt(); 603 604 switch (type) { 605 case VAL_NULL: 606 return null; 607 608 case VAL_STRING: 609 return readString(); 610 611 case VAL_INTEGER: 612 return readInt(); 613 614 case VAL_MAP: 615 return readHashMap(loader); 616 617 case VAL_PARCELABLE: 618 return readParcelable(loader); 619 620 case VAL_SHORT: 621 return (short) readInt(); 622 623 case VAL_LONG: 624 return readLong(); 625 626 case VAL_FLOAT: 627 return readFloat(); 628 629 case VAL_DOUBLE: 630 return readDouble(); 631 632 case VAL_BOOLEAN: 633 return readInt() == 1; 634 635 case VAL_CHARSEQUENCE: 636 return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(realParcel); 637 638 case VAL_BOOLEANARRAY: 639 return createBooleanArray(); 640 641 case VAL_BYTEARRAY: 642 return createByteArray(); 643 644 case VAL_STRINGARRAY: 645 return createStringArray(); 646 647 case VAL_OBJECTARRAY: 648 return readArray(loader); 649 650 case VAL_INTARRAY: 651 return createIntArray(); 652 653 case VAL_LONGARRAY: 654 return createLongArray(); 655 656 case VAL_BYTE: 657 return readByte(); 658 659 case VAL_BUNDLE: 660 return readBundle(loader); // loading will be deferred 661 662 default: 663 int off = dataPosition() - 4; 664 throw new RuntimeException( 665 "Parcel " + this + ": Unmarshalling unknown type code " + type 666 + " at offset " + off); 667 } 668 } 669 670 @Implementation 671 public Bundle readBundle() { 672 return readBundle(null); 673 } 674 675 @Implementation 676 public Bundle readBundle(ClassLoader loader) { 677 int offset = dataPosition(); 678 int N = readInt(); 679 if (N < 0) { 680 return null; 681 } 682 int magic = readInt(); 683 if (magic != 0x4C444E42) { 684 throw new RuntimeException("Magic number missing from bundle stream"); 685 } 686 687 Bundle bundle = new Bundle(); 688 689 // Read map 690 HashMap m = new HashMap(); 691 readMap(m, null); 692 693 shadowOf(bundle).map.putAll(m); 694 695 return bundle; 696 } 697 698 @Implementation 699 public void writeBundle(Bundle val) { 700 if (val == null) { 701 writeInt(-1); 702 return; 703 } 704 705 writeInt(-1); // dummy, will hold length 706 int oldPos = dataPosition(); 707 writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' 708 709 writeMapInternal(shadowOf(val).map); 710 int newPos = dataPosition(); 711 712 // Backpatch length 713 setDataPosition(oldPos - 4); 714 int N = newPos - oldPos; 715 writeInt(N); 716 setDataPosition(newPos); 717 } 718 719 @Implementation 720 public void writeParcelable(Parcelable p, int flags) { 721 if (p == null) { 722 writeString(null); 723 return; 724 } 725 String name = p.getClass().getName(); 726 writeString(name); 727 p.writeToParcel(realParcel, flags); 728 } 729 730 @Implementation 731 public <T extends Parcelable> T readParcelable(ClassLoader loader) { 732 String name = readString(); 733 if (name == null) { 734 return null; 735 } 736 Parcelable.Creator<T> creator; 737 try { 738 Class c = loader == null ? Class.forName(name) : Class.forName(name, true, loader); 739 740 // Use CREATOR from Shadow for Robolectric mocked classes. 741 Class shadowClass = ShadowWrangler.getInstance().findShadowClass(c, loader); 742 if (shadowClass != null) { 743 c = shadowClass; 744 name = shadowClass.getName(); 745 } 746 747 Field f = c.getField("CREATOR"); 748 creator = (Parcelable.Creator) f.get(null); 749 } catch (IllegalAccessException e) { 750 Log.e("Parcel", "Class not found when unmarshalling: " + name + ", e: " + e); 751 throw new RuntimeException("IllegalAccessException when unmarshalling: " + name); 752 } catch (ClassNotFoundException e) { 753 Log.e("Parcel", "Class not found when unmarshalling: " + name + ", e: " + e); 754 throw new RuntimeException("ClassNotFoundException when unmarshalling: " + name); 755 } catch (ClassCastException e) { 756 throw new RuntimeException("Parcelable protocol requires a " 757 + "Parcelable.Creator object called " + " CREATOR on class " + name); 758 } catch (NoSuchFieldException e) { 759 throw new RuntimeException("Parcelable protocol requires a " 760 + "Parcelable.Creator object called " + " CREATOR on class " + name); 761 } 762 if (creator == null) { 763 throw new RuntimeException("Parcelable protocol requires a " 764 + "Parcelable.Creator object called " + " CREATOR on class " + name); 765 } 766 767 return creator.createFromParcel(realParcel); 768 } 769 770 @Implementation 771 public ArrayList createTypedArrayList(Parcelable.Creator c) { 772 int N = readInt(); 773 if (N < 0) { 774 return null; 775 } 776 777 ArrayList l = new ArrayList(N); 778 779 while (N > 0) { 780 if (readInt() != 0) { 781 l.add(c.createFromParcel(realParcel)); 782 } else { 783 l.add(null); 784 } 785 N--; 786 } 787 return l; 788 } 789 790 @Implementation 791 public void writeTypedList(List val) { 792 if (val == null) { 793 writeInt(-1); 794 return; 795 } 796 797 int N = val.size(); 798 int i = 0; 799 writeInt(N); 800 while (i < N) { 801 Object item = val.get(i); 802 if (item != null) { 803 writeInt(1); 804 ((Parcelable) item).writeToParcel(realParcel, 0); 805 } else { 806 writeInt(0); 807 } 808 i++; 809 } 810 } 811 812 @Implementation 813 public void writeMap(Map val) { 814 writeMapInternal(val); 815 } 816 817 @Implementation 818 public void readMap(Map outVal, ClassLoader loader) { 819 int N = readInt(); 820 readMapInternal(outVal, N, loader); 821 } 822 823 @Implementation 824 public HashMap readHashMap(ClassLoader loader) { 825 int N = readInt(); 826 if (N < 0) { 827 return null; 828 } 829 HashMap m = new HashMap(N); 830 readMapInternal(m, N, loader); 831 return m; 832 } 833 834 private void writeMapInternal(Map<String, Object> val) { 835 if (val == null) { 836 writeInt(-1); 837 return; 838 } 839 840 Set<Map.Entry<String, Object>> entries = val.entrySet(); 841 writeInt(entries.size()); 842 for (Map.Entry<String, Object> e : entries) { 843 writeValue(e.getKey()); 844 writeValue(e.getValue()); 845 } 846 } 847 848 private void readMapInternal(Map outVal, int N, ClassLoader loader) { 849 for (int i = 0; i < N; i++) { 850 Object key = readValue(loader); 851 Object value = readValue(loader); 852 outVal.put(key, value); 853 } 854 } 855 856 private void readArrayInternal(Object[] outVal, int N, ClassLoader loader) { 857 for (int i = 0; i < N; i++) { 858 Object value = readValue(loader); 859 // Log.d("Parcel", "Unmarshalling value=" + value); 860 outVal[i] = value; 861 } 862 } 863 864 private void addValueToList(Pair<Integer, ?> value) { 865 if (index < parcelData.size()) { 866 parcelData.set(index, value); 867 } else { 868 parcelData.add(value); 869 } 870 index++; 871 } 872 873 private <T extends Object> T readValueFromList(T defaultValue) { 874 if (index < parcelData.size()) { 875 return (T) parcelData.get(index++).second; 876 } else { 877 return defaultValue; 878 } 879 } 880 881 public int getIndex() { 882 return index; 883 } 884 885 public List getParcelData() { 886 return parcelData; 887 } 888 } 889