1package com.xtremelabs.robolectric.shadows;
2
3import static com.xtremelabs.robolectric.Robolectric.shadowOf;
4
5import android.os.Bundle;
6import android.os.Parcel;
7import android.os.Parcelable;
8import android.text.TextUtils;
9import android.util.Log;
10import android.util.Pair;
11
12import com.xtremelabs.robolectric.Robolectric;
13import com.xtremelabs.robolectric.bytecode.ShadowWrangler;
14import com.xtremelabs.robolectric.internal.Implementation;
15import com.xtremelabs.robolectric.internal.Implements;
16import com.xtremelabs.robolectric.internal.RealObject;
17
18import java.lang.reflect.Field;
19import java.util.ArrayList;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24
25@Implements(Parcel.class)
26@SuppressWarnings("unchecked")
27public 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