Skip to content

Commit

Permalink
Merge pull request #737 from yomik/fix/android-parcelable-superclass-602
Browse files Browse the repository at this point in the history
Writing to /Reading from Parcelable ignores superclass

Closes #602
  • Loading branch information
joelittlejohn authored Jun 6, 2017
2 parents dcaa7d7 + bc40c40 commit f8da2ee
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ private void addParcelSupport(JDefinedClass jclass) {
parcelableHelper.addWriteToParcel(jclass);
parcelableHelper.addDescribeContents(jclass);
parcelableHelper.addCreator(jclass);
parcelableHelper.addConstructorFromParcel(jclass);
// Add empty constructor
jclass.constructor(JMod.PUBLIC);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ public void addWriteToParcel(JDefinedClass jclass) {
JMethod method = jclass.method(JMod.PUBLIC, void.class, "writeToParcel");
JVar dest = method.param(Parcel.class, "dest");
method.param(int.class, "flags");


// Call super.writeToParcel
if (extendsParcelable(jclass)) {
method.body().directStatement("super.writeToParcel(dest, flags);");
}
for (JFieldVar f : jclass.fields().values()) {
if( (f.mods().getValue() & JMod.STATIC) == JMod.STATIC ) {
continue;
Expand Down Expand Up @@ -57,29 +61,25 @@ public void addCreator(JDefinedClass jclass) {
creatorField.init(JExpr._new(creatorClass));
}

private void addNewArray(JDefinedClass jclass, JDefinedClass creatorClass) {
JMethod newArray = creatorClass.method(JMod.PUBLIC, jclass.array(), "newArray");
newArray.param(int.class, "size");
newArray.body()._return(JExpr.direct("new " + jclass.name() + "[size]"));
}

private void addCreateFromParcel(JDefinedClass jclass, JDefinedClass creatorClass) {
JMethod createFromParcel = creatorClass.method(JMod.PUBLIC, jclass, "createFromParcel");
JVar in = createFromParcel.param(Parcel.class, "in");
JVar instance = createFromParcel.body().decl(jclass, "instance", JExpr._new(jclass));
suppressWarnings(createFromParcel, "unchecked");
public void addConstructorFromParcel(JDefinedClass jclass) {
JMethod ctorFromParcel = jclass.constructor(JMod.PROTECTED);
JVar in = ctorFromParcel.param(Parcel.class, "in");
// Call super(in)
if (extendsParcelable(jclass)) {
ctorFromParcel.body().directStatement("super(in);");
}
for (JFieldVar f : jclass.fields().values()) {
if( (f.mods().getValue() & JMod.STATIC) == JMod.STATIC ) {
continue;
}
if (f.type().erasure().name().equals("List")) {
createFromParcel.body()
ctorFromParcel.body()
.invoke(in, "readList")
.arg(instance.ref(f))
.arg(JExpr._this().ref(f))
.arg(JExpr.direct(getListType(f.type()) + ".class.getClassLoader()"));
} else {
createFromParcel.body().assign(
instance.ref(f),
ctorFromParcel.body().assign(
JExpr._this().ref(f),
JExpr.cast(
f.type(),
in.invoke("readValue").arg(JExpr.direct(f.type().erasure().name() + ".class.getClassLoader()"))
Expand All @@ -88,7 +88,33 @@ private void addCreateFromParcel(JDefinedClass jclass, JDefinedClass creatorClas
}

}
createFromParcel.body()._return(instance);
}


private void addNewArray(JDefinedClass jclass, JDefinedClass creatorClass) {
JMethod newArray = creatorClass.method(JMod.PUBLIC, jclass.array(), "newArray");
newArray.param(int.class, "size");
newArray.body()._return(JExpr.direct("new " + jclass.name() + "[size]"));
}

private void addCreateFromParcel(JDefinedClass jclass, JDefinedClass creatorClass) {
JMethod createFromParcel = creatorClass.method(JMod.PUBLIC, jclass, "createFromParcel");
JVar in = createFromParcel.param(Parcel.class, "in");
suppressWarnings(createFromParcel, "unchecked");
createFromParcel.body()._return(JExpr._new(jclass).arg(in));
}

private boolean extendsParcelable(final JDefinedClass jclass) {
final java.util.Iterator<JClass> interfaces = jclass._extends() != null ? jclass._extends()._implements() : null;
if (interfaces != null) {
while (interfaces.hasNext()) {
final JClass iface = interfaces.next();
if (iface.erasure().name().equals("Parcelable")) {
return true;
}
}
}
return false;
}

private String getListType(JType jType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,16 @@ public void parcelableTreeIsParcelable() throws ClassNotFoundException, IOExcept
assertThat(instance, is(equalTo(unparceledInstance)));
}

@Test
public void parcelableSuperclassIsUnparceled() throws ClassNotFoundException, IOException {
Class<?> parcelableType = schemaRule.generateAndCompile("/schema/parcelable/parcelable-superclass-schema.json", "com.example",
config("parcelable", true))
.loadClass("com.example.ParcelableSuperclassSchema");

Parcelable instance = (Parcelable) new ObjectMapper().readValue(ParcelableIT.class.getResourceAsStream("/schema/parcelable/parcelable-superclass-data.json"), parcelableType);
Parcel parcel = parcelableWriteToParcel(instance);
Parcelable unparceledInstance = parcelableReadFromParcel(parcel, parcelableType, instance);

assertThat(instance, is(equalTo(unparceledInstance)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public static Parcel writeToParcel(Parcelable instance, String key) {
parcel.writeBundle(bundle);
return parcel;
}

public static Parcel parcelableWriteToParcel(Parcelable instance) {
Parcel parcel = Parcel.obtain();
instance.writeToParcel(parcel, 0);
return parcel;
}

public static Parcelable readFromParcel(Parcel parcel, Class<?> parcelableType, String key) {
parcel.setDataPosition(0);
Expand All @@ -44,4 +50,25 @@ public static Parcelable readFromParcel(Parcel parcel, Class<?> parcelableType,
return unparceledInstance;
}

public static Parcelable parcelableReadFromParcel(Parcel parcel, Class<?> parcelableType, Parcelable parcelable) {
parcel.setDataPosition(0);
return createFromParcelFromParcelable(parcel, parcelable);
}


private static Parcelable createFromParcelFromParcelable(Parcel in, Parcelable parcelable) {
try {
Class<?> parcelableClass = parcelable.getClass().getClassLoader().loadClass(parcelable.getClass().getName());
java.lang.reflect.Field creatorField = parcelableClass.getDeclaredField("CREATOR");
Object creatorInstance = creatorField.get(parcelable);
java.lang.reflect.Method createFromParcel = creatorInstance.getClass().getDeclaredMethod("createFromParcel", new Class[] { Parcel.class });
createFromParcel.setAccessible(true);
return (Parcelable) createFromParcel.invoke(creatorInstance, in);
}
catch (Throwable ignored) {
ignored.getCause().printStackTrace();
// Ignore
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"baseBooleanProperty": true,
"baseStringProperty": "basestring",
"baseIntegerProperty": 1234,
"booleanProperty" : true,
"stringProperty" : "aaa",
"integerProperty" : 10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "object",
"properties": {
"baseBooleanProperty" : {
"type" : "boolean",
"default": false
},
"baseStringProperty" : {
"type" : "string"
},
"baseIntegerProperty" : {
"type" : "integer"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"type" : "object",
"extends": {
"$ref": "parcelable-superclass-schema-base.json"
},
"properties" : {
"booleanProperty" : {
"type" : "boolean"
},
"stringProperty" : {
"type" : "string"
},
"integerProperty" : {
"type" : "integer"
},
"baseIntegerProperty": {
"title": "Tests overriding of property",
"type": "integer"
}
}
}

0 comments on commit f8da2ee

Please sign in to comment.