Commit 9aec1880 authored by Bruno Coelho's avatar Bruno Coelho Committed by Xianwen Chen

Android parcelable support (#308)

parent 46205049
...@@ -232,7 +232,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { ...@@ -232,7 +232,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) {
writeHppFile(cppName, origin, refs.hpp, refs.hppFwds, writeCppPrototype) writeHppFile(cppName, origin, refs.hpp, refs.hppFwds, writeCppPrototype)
if (r.consts.nonEmpty || r.derivingTypes.nonEmpty) { if (r.consts.nonEmpty || r.derivingTypes.contains(DerivingType.Eq) || r.derivingTypes.contains(DerivingType.Ord)) {
writeCppFile(cppName, origin, refs.cpp, w => { writeCppFile(cppName, origin, refs.cpp, w => {
generateCppConstants(w, r.consts, actualSelf) generateCppConstants(w, r.consts, actualSelf)
......
...@@ -220,13 +220,13 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -220,13 +220,13 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
javaAnnotationHeader.foreach(w.wl) javaAnnotationHeader.foreach(w.wl)
val self = marshal.typename(javaName, r) val self = marshal.typename(javaName, r)
val comparableFlag = val interfaces = scala.collection.mutable.ArrayBuffer[String]()
if (r.derivingTypes.contains(DerivingType.Ord)) { if (r.derivingTypes.contains(DerivingType.Ord))
s" implements Comparable<$self>" interfaces += s"Comparable<$self>"
} else { if (spec.javaImplementAndroidOsParcelable && r.derivingTypes.contains(DerivingType.AndroidParcelable))
"" interfaces += "android.os.Parcelable"
} val implementsSection = if (interfaces.isEmpty) "" else " implements " + interfaces.mkString(", ")
w.w(s"${javaClassAccessModifierString}${javaFinal}class ${self + javaTypeParams(params)}$comparableFlag").braced { w.w(s"${javaClassAccessModifierString}${javaFinal}class ${self + javaTypeParams(params)}$implementsSection").braced {
w.wl w.wl
generateJavaConstants(w, r.consts) generateJavaConstants(w, r.consts)
// Field definitions. // Field definitions.
...@@ -358,6 +358,9 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -358,6 +358,9 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
} }
w.wl w.wl
if (spec.javaImplementAndroidOsParcelable && r.derivingTypes.contains(DerivingType.AndroidParcelable))
writeParcelable(w, self, r);
if (r.derivingTypes.contains(DerivingType.Ord)) { if (r.derivingTypes.contains(DerivingType.Ord)) {
def primitiveCompare(ident: Ident) { def primitiveCompare(ident: Ident) {
w.wl(s"if (this.${idJava.field(ident)} < other.${idJava.field(ident)}) {").nested { w.wl(s"if (this.${idJava.field(ident)} < other.${idJava.field(ident)}) {").nested {
...@@ -407,4 +410,151 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ...@@ -407,4 +410,151 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
def javaTypeParams(params: Seq[TypeParam]): String = def javaTypeParams(params: Seq[TypeParam]): String =
if (params.isEmpty) "" else params.map(p => idJava.typeParam(p.ident)).mkString("<", ", ", ">") if (params.isEmpty) "" else params.map(p => idJava.typeParam(p.ident)).mkString("<", ", ", ">")
def writeParcelable(w: IndentWriter, self: String, r: Record) = {
// Generates the methods and the constructor to implement the interface android.os.Parcelable
// CREATOR
w.wl
w.wl(s"public static final android.os.Parcelable.Creator<$self> CREATOR")
w.w(s" = new android.os.Parcelable.Creator<$self>()").bracedSemi {
w.wl("@Override")
w.w(s"public $self createFromParcel(android.os.Parcel in)").braced {
w.wl(s"return new $self(in);")
}
w.wl
w.wl("@Override")
w.w(s"public $self[] newArray(int size)").braced {
w.wl(s"return new $self[size];")
}
}
// constructor (Parcel)
def deserializeField(f: Field, m: Meta, inOptional: Boolean) {
m match {
case MString => w.wl(s"this.${idJava.field(f.ident)} = in.readString();")
case MBinary => {
w.wl(s"this.${idJava.field(f.ident)} = in.createByteArray();")
}
case MDate => w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(in.readLong());")
case t: MPrimitive => t.jName match {
case "short" => w.wl(s"this.${idJava.field(f.ident)} = (short)in.readInt();")
case "int" => w.wl(s"this.${idJava.field(f.ident)} = in.readInt();")
case "long" => w.wl(s"this.${idJava.field(f.ident)} = in.readLong();")
case "byte" => w.wl(s"this.${idJava.field(f.ident)} = in.readByte();")
case "boolean" => w.wl(s"this.${idJava.field(f.ident)} = in.readByte() != 0;")
case "float" => w.wl(s"this.${idJava.field(f.ident)} = in.readFloat();")
case "double" => w.wl(s"this.${idJava.field(f.ident)} = in.readDouble();")
case _ => throw new AssertionError("Unreachable")
}
case df: MDef => df.defType match {
case DRecord => w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(in);")
case DEnum => w.wl(s"this.${idJava.field(f.ident)} = ${marshal.typename(f.ty)}.values()[in.readInt()];")
case _ => throw new AssertionError("Unreachable")
}
case e: MExtern => e.defType match {
case DRecord => w.wl(s"this.${idJava.field(f.ident)} = ${e.java.readFromParcel.format(marshal.typename(f.ty))};")
case DEnum => w.wl(s"this.${idJava.field(f.ident)} = ${marshal.typename(f.ty)}.values()[in.readInt()];")
case _ => throw new AssertionError("Unreachable")
}
case MList => {
w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}();")
w.wl(s"in.readList(this.${idJava.field(f.ident)}, getClass().getClassLoader());")
}
case MSet => {
val collectionTypeName = marshal.typename(f.ty).replaceFirst("HashSet<(.*)>", "$1")
w.wl(s"ArrayList<${collectionTypeName}> ${idJava.field(f.ident)}Temp = new ArrayList<${collectionTypeName}>();")
w.wl(s"in.readList(${idJava.field(f.ident)}Temp, getClass().getClassLoader());")
w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(${idJava.field(f.ident)}Temp);")
}
case MMap => {
w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}();")
w.wl(s"in.readMap(this.${idJava.field(f.ident)}, getClass().getClassLoader());")
}
case MOptional => {
if (inOptional)
throw new AssertionError("nested optional?")
w.wl("if (in.readByte() == 0) {").nested {
w.wl(s"this.${idJava.field(f.ident)} = null;")
}
w.wl("} else {").nested {
deserializeField(f, f.ty.resolved.args.head.base, true)
}
w.wl("}")
}
case _ => throw new AssertionError("Unreachable")
}
}
w.wl
w.w(s"public $self(android.os.Parcel in)").braced {
for (f <- r.fields)
deserializeField(f, f.ty.resolved.base, false)
}
// describeContents
w.wl
w.wl("@Override")
w.w("public int describeContents()").braced {
w.wl("return 0;")
}
// writeToParcel
def serializeField(f: Field, m: Meta, inOptional: Boolean) {
m match {
case MString => w.wl(s"out.writeString(this.${idJava.field(f.ident)});")
case MBinary => {
w.wl(s"out.writeByteArray(this.${idJava.field(f.ident)});")
}
case MDate => w.wl(s"out.writeLong(this.${idJava.field(f.ident)}.getTime());")
case t: MPrimitive => t.jName match {
case "short" | "int" => w.wl(s"out.writeInt(this.${idJava.field(f.ident)});")
case "long" => w.wl(s"out.writeLong(this.${idJava.field(f.ident)});")
case "byte" => w.wl(s"out.writeByte(this.${idJava.field(f.ident)});")
case "boolean" => w.wl(s"out.writeByte(this.${idJava.field(f.ident)} ? (byte)1 : 0);")
case "float" => w.wl(s"out.writeFloat(this.${idJava.field(f.ident)});")
case "double" => w.wl(s"out.writeDouble(this.${idJava.field(f.ident)});")
case _ => throw new AssertionError("Unreachable")
}
case df: MDef => df.defType match {
case DRecord => w.wl(s"this.${idJava.field(f.ident)}.writeToParcel(out, flags);")
case DEnum => w.wl(s"out.writeInt(this.${idJava.field(f.ident)}.ordinal());")
case _ => throw new AssertionError("Unreachable")
}
case e: MExtern => e.defType match {
case DRecord => w.wl(e.java.writeToParcel.format(idJava.field(f.ident)) + ";")
case DEnum => w.wl(s"out.writeInt((int)this.${idJava.field(f.ident)});")
case _ => throw new AssertionError("Unreachable")
}
case MList => {
w.wl(s"out.writeList(this.${idJava.field(f.ident)});")
}
case MSet => {
val collectionTypeName = marshal.typename(f.ty).replaceFirst("HashSet<(.*)>", "$1")
w.wl(s"out.writeList(new ArrayList<${collectionTypeName}>(this.${idJava.field(f.ident)}));")
}
case MMap => w.wl(s"out.writeMap(this.${idJava.field(f.ident)});")
case MOptional => {
if (inOptional)
throw new AssertionError("nested optional?")
w.wl(s"if (this.${idJava.field(f.ident)} != null) {").nested {
w.wl("out.writeByte((byte)1);")
serializeField(f, f.ty.resolved.args.head.base, true)
}
w.wl("} else {").nested {
w.wl("out.writeByte((byte)0);")
}
w.wl("}")
}
case _ => throw new AssertionError("Unreachable")
}
}
w.wl
w.wl("@Override")
w.w("public void writeToParcel(android.os.Parcel out, int flags)").braced {
for (f <- r.fields)
serializeField(f, f.ty.resolved.base, false)
}
w.wl
}
} }
...@@ -44,6 +44,7 @@ object Main { ...@@ -44,6 +44,7 @@ object Main {
var javaAnnotation: Option[String] = None var javaAnnotation: Option[String] = None
var javaNullableAnnotation: Option[String] = None var javaNullableAnnotation: Option[String] = None
var javaNonnullAnnotation: Option[String] = None var javaNonnullAnnotation: Option[String] = None
var javaImplementAndroidOsParcelable : Boolean = false
var javaUseFinalForRecord: Boolean = true var javaUseFinalForRecord: Boolean = true
var jniOutFolder: Option[File] = None var jniOutFolder: Option[File] = None
var jniHeaderOutFolderOptional: Option[File] = None var jniHeaderOutFolderOptional: Option[File] = None
...@@ -114,6 +115,8 @@ object Main { ...@@ -114,6 +115,8 @@ object Main {
.text("Java annotation (@Nullable) to place on all fields and return values that are optional") .text("Java annotation (@Nullable) to place on all fields and return values that are optional")
opt[String]("java-nonnull-annotation").valueName("<nonnull-annotation-class>").foreach(x => javaNonnullAnnotation = Some(x)) opt[String]("java-nonnull-annotation").valueName("<nonnull-annotation-class>").foreach(x => javaNonnullAnnotation = Some(x))
.text("Java annotation (@Nonnull) to place on all fields and return values that are not optional") .text("Java annotation (@Nonnull) to place on all fields and return values that are not optional")
opt[Boolean]("java-implement-android-os-parcelable").valueName("<true/false>").foreach(x => javaImplementAndroidOsParcelable = x)
.text("all generated java classes will implement the interface android.os.Parcelable")
opt[Boolean]("java-use-final-for-record").valueName("<use-final-for-record>").foreach(x => javaUseFinalForRecord = x) opt[Boolean]("java-use-final-for-record").valueName("<use-final-for-record>").foreach(x => javaUseFinalForRecord = x)
.text("Whether generated Java classes for records should be marked 'final' (default: true). ") .text("Whether generated Java classes for records should be marked 'final' (default: true). ")
note("") note("")
...@@ -303,6 +306,7 @@ object Main { ...@@ -303,6 +306,7 @@ object Main {
javaAnnotation, javaAnnotation,
javaNullableAnnotation, javaNullableAnnotation,
javaNonnullAnnotation, javaNonnullAnnotation,
javaImplementAndroidOsParcelable,
javaUseFinalForRecord, javaUseFinalForRecord,
cppOutFolder, cppOutFolder,
cppHeaderOutFolder, cppHeaderOutFolder,
......
...@@ -100,6 +100,7 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { ...@@ -100,6 +100,7 @@ class YamlGenerator(spec: Spec) extends Generator(spec) {
r.derivingTypes.collect { r.derivingTypes.collect {
case Record.DerivingType.Eq => "eq" case Record.DerivingType.Eq => "eq"
case Record.DerivingType.Ord => "ord" case Record.DerivingType.Ord => "ord"
case Record.DerivingType.AndroidParcelable => "parcelable"
}.mkString(" deriving(", ", ", ")") }.mkString(" deriving(", ", ", ")")
} }
} }
...@@ -135,7 +136,9 @@ class YamlGenerator(spec: Spec) extends Generator(spec) { ...@@ -135,7 +136,9 @@ class YamlGenerator(spec: Spec) extends Generator(spec) {
"boxed" -> QuotedString(javaMarshal.fqTypename(td.ident, td.body)), "boxed" -> QuotedString(javaMarshal.fqTypename(td.ident, td.body)),
"reference" -> javaMarshal.isReference(td), "reference" -> javaMarshal.isReference(td),
"generic" -> true, "generic" -> true,
"hash" -> QuotedString("%s.hashCode()") "hash" -> QuotedString("%s.hashCode()"),
"writeToParcel" -> QuotedString("%s.writeToParcel(out, flags)"),
"readFromParcel" -> QuotedString("new %s(in)")
) )
private def jni(td: TypeDecl) = Map[String, Any]( private def jni(td: TypeDecl) = Map[String, Any](
...@@ -205,7 +208,9 @@ object YamlGenerator { ...@@ -205,7 +208,9 @@ object YamlGenerator {
nested(td, "java")("boxed").toString, nested(td, "java")("boxed").toString,
nested(td, "java")("reference").asInstanceOf[Boolean], nested(td, "java")("reference").asInstanceOf[Boolean],
nested(td, "java")("generic").asInstanceOf[Boolean], nested(td, "java")("generic").asInstanceOf[Boolean],
nested(td, "java")("hash").toString), nested(td, "java")("hash").toString,
if (nested(td, "java") contains "writeToParcel") nested(td, "java")("writeToParcel").toString else "%s.writeToParcel(out, flags)",
if (nested(td, "java") contains "readFromParcel") nested(td, "java")("readFromParcel").toString else "new %s(in)"),
MExtern.Jni( MExtern.Jni(
nested(td, "jni")("translator").toString, nested(td, "jni")("translator").toString,
nested(td, "jni")("header").toString, nested(td, "jni")("header").toString,
......
...@@ -75,7 +75,7 @@ case class Record(ext: Ext, fields: Seq[Field], consts: Seq[Const], derivingType ...@@ -75,7 +75,7 @@ case class Record(ext: Ext, fields: Seq[Field], consts: Seq[Const], derivingType
object Record { object Record {
object DerivingType extends Enumeration { object DerivingType extends Enumeration {
type DerivingType = Value type DerivingType = Value
val Eq, Ord = Value val Eq, Ord, AndroidParcelable = Value
} }
} }
......
...@@ -36,6 +36,7 @@ package object generatorTools { ...@@ -36,6 +36,7 @@ package object generatorTools {
javaAnnotation: Option[String], javaAnnotation: Option[String],
javaNullableAnnotation: Option[String], javaNullableAnnotation: Option[String],
javaNonnullAnnotation: Option[String], javaNonnullAnnotation: Option[String],
javaImplementAndroidOsParcelable: Boolean,
javaUseFinalForRecord: Boolean, javaUseFinalForRecord: Boolean,
cppOutFolder: Option[File], cppOutFolder: Option[File],
cppHeaderOutFolder: Option[File], cppHeaderOutFolder: Option[File],
......
...@@ -57,7 +57,9 @@ object MExtern { ...@@ -57,7 +57,9 @@ object MExtern {
boxed: String, // Java typename used if boxing is required, must be an object. boxed: String, // Java typename used if boxing is required, must be an object.
reference: Boolean, // True if the unboxed type is an object reference and qualifies for any kind of "nonnull" annotation in Java. Only used for "record" types. reference: Boolean, // True if the unboxed type is an object reference and qualifies for any kind of "nonnull" annotation in Java. Only used for "record" types.
generic: Boolean, // Set to false to exclude type arguments from the Java class. This is should be true by default. Useful if template arguments are only used in C++. generic: Boolean, // Set to false to exclude type arguments from the Java class. This is should be true by default. Useful if template arguments are only used in C++.
hash: String // A well-formed expression to get the hash value. Must be a format string with a single "%s" placeholder. Only used for "record" types types with "eq" deriving when needed. hash: String, // A well-formed expression to get the hash value. Must be a format string with a single "%s" placeholder. Only used for "record" types types with "eq" deriving when needed.
writeToParcel: String, // A well-formed expression to write value into android.os.Parcel. Must be a format string with a single "%s" placeholder. Only used for "record" types types
readFromParcel: String // A well-formed expression to read value from android.os.Parcel. Must be a format string with a single "%s" placeholder. Only used for "record" types types
) )
case class Jni( case class Jni(
translator: String, // C++ typename containing toCpp/fromCpp methods translator: String, // C++ typename containing toCpp/fromCpp methods
......
...@@ -123,6 +123,7 @@ private object IdlParser extends RegexParsers { ...@@ -123,6 +123,7 @@ private object IdlParser extends RegexParsers {
_.map(ident => ident.name match { _.map(ident => ident.name match {
case "eq" => Record.DerivingType.Eq case "eq" => Record.DerivingType.Eq
case "ord" => Record.DerivingType.Ord case "ord" => Record.DerivingType.Ord
case "parcelable" => Record.DerivingType.AndroidParcelable
case _ => return err( s"""Unrecognized deriving type "${ident.name}"""") case _ => return err( s"""Unrecognized deriving type "${ident.name}"""")
}).toSet }).toSet
} }
......
...@@ -17,7 +17,7 @@ enum_usage_record = record { ...@@ -17,7 +17,7 @@ enum_usage_record = record {
l: list<color>; l: list<color>;
s: set<color>; s: set<color>;
m: map<color, color>; m: map<color, color>;
} } deriving(parcelable)
enum_usage_interface = interface +c +j +o { enum_usage_interface = interface +c +j +o {
e(e: color): color; e(e: color): color;
......
nested_collection = record { nested_collection = record {
set_list : list<set<string>>; set_list : list<set<string>>;
} } deriving(parcelable)
...@@ -14,4 +14,4 @@ assorted_primitives = record { ...@@ -14,4 +14,4 @@ assorted_primitives = record {
o_sixtyfour: optional<i64>; o_sixtyfour: optional<i64>;
o_fthirtytwo: optional<f32>; o_fthirtytwo: optional<f32>;
o_fsixtyfour: optional<f64>; o_fsixtyfour: optional<f64>;
} deriving (eq) } deriving (eq, parcelable)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
date_record = record { date_record = record {
created_at: extern_date; created_at: extern_date;
} deriving(eq, ord) } deriving(eq, ord, parcelable)
map_date_record = record { map_date_record = record {
dates_by_id: map<string, extern_date>; dates_by_id: map<string, extern_date>;
......
# This is an example YAML file mimicking the builtin "date" type as external type # This is an example YAML file mimicking the builtin "date" type as external type
--- ---
name: extern_date name: extern_date
typedef: 'record deriving(eq, ord)' typedef: 'record deriving(eq, ord, parcelable)'
params: [] params: []
prefix: '' prefix: ''
cpp: cpp:
...@@ -23,6 +23,8 @@ java: ...@@ -23,6 +23,8 @@ java:
reference: true reference: true
generic: true generic: true
hash: '%s.hashCode()' hash: '%s.hashCode()'
writeToParcel: 'out.writeLong(%s.getTime())'
readFromParcel: 'new %s(in.readLong())'
jni: jni:
translator: '::djinni::Date' translator: '::djinni::Date'
header: '"Marshal.hpp"' header: '"Marshal.hpp"'
......
...@@ -6,7 +6,7 @@ package com.dropbox.djinni.test; ...@@ -6,7 +6,7 @@ package com.dropbox.djinni.test;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class AssortedPrimitives { public class AssortedPrimitives implements android.os.Parcelable {
/*package*/ final boolean mB; /*package*/ final boolean mB;
...@@ -194,4 +194,121 @@ public class AssortedPrimitives { ...@@ -194,4 +194,121 @@ public class AssortedPrimitives {
"}"; "}";
} }
public static final android.os.Parcelable.Creator<AssortedPrimitives> CREATOR
= new android.os.Parcelable.Creator<AssortedPrimitives>() {
@Override
public AssortedPrimitives createFromParcel(android.os.Parcel in) {
return new AssortedPrimitives(in);
}
@Override
public AssortedPrimitives[] newArray(int size) {
return new AssortedPrimitives[size];
}
};
public AssortedPrimitives(android.os.Parcel in) {
this.mB = in.readByte() != 0;
this.mEight = in.readByte();
this.mSixteen = (short)in.readInt();
this.mThirtytwo = in.readInt();
this.mSixtyfour = in.readLong();
this.mFthirtytwo = in.readFloat();
this.mFsixtyfour = in.readDouble();
if (in.readByte() == 0) {
this.mOB = null;
} else {
this.mOB = in.readByte() != 0;
}
if (in.readByte() == 0) {
this.mOEight = null;
} else {
this.mOEight = in.readByte();
}
if (in.readByte() == 0) {
this.mOSixteen = null;
} else {
this.mOSixteen = (short)in.readInt();
}
if (in.readByte() == 0) {
this.mOThirtytwo = null;
} else {
this.mOThirtytwo = in.readInt();
}
if (in.readByte() == 0) {
this.mOSixtyfour = null;
} else {
this.mOSixtyfour = in.readLong();
}
if (in.readByte() == 0) {
this.mOFthirtytwo = null;
} else {
this.mOFthirtytwo = in.readFloat();
}
if (in.readByte() == 0) {
this.mOFsixtyfour = null;
} else {
this.mOFsixtyfour = in.readDouble();
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(android.os.Parcel out, int flags) {
out.writeByte(this.mB ? (byte)1 : 0);
out.writeByte(this.mEight);
out.writeInt(this.mSixteen);
out.writeInt(this.mThirtytwo);
out.writeLong(this.mSixtyfour);
out.writeFloat(this.mFthirtytwo);
out.writeDouble(this.mFsixtyfour);
if (this.mOB != null) {
out.writeByte((byte)1);
out.writeByte(this.mOB ? (byte)1 : 0);
} else {
out.writeByte((byte)0);
}
if (this.mOEight != null) {
out.writeByte((byte)1);
out.writeByte(this.mOEight);
} else {
out.writeByte((byte)0);
}
if (this.mOSixteen != null) {
out.writeByte((byte)1);
out.writeInt(this.mOSixteen);
} else {
out.writeByte((byte)0);
}
if (this.mOThirtytwo != null) {
out.writeByte((byte)1);
out.writeInt(this.mOThirtytwo);
} else {
out.writeByte((byte)0);
}
if (this.mOSixtyfour != null) {
out.writeByte((byte)1);
out.writeLong(this.mOSixtyfour);
} else {
out.writeByte((byte)0);
}
if (this.mOFthirtytwo != null) {
out.writeByte((byte)1);
out.writeFloat(this.mOFthirtytwo);
} else {
out.writeByte((byte)0);
}
if (this.mOFsixtyfour != null) {
out.writeByte((byte)1);
out.writeDouble(this.mOFsixtyfour);
} else {
out.writeByte((byte)0);
}
}
} }
...@@ -6,7 +6,7 @@ package com.dropbox.djinni.test; ...@@ -6,7 +6,7 @@ package com.dropbox.djinni.test;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class DateRecord implements Comparable<DateRecord> { public class DateRecord implements Comparable<DateRecord>, android.os.Parcelable {
/*package*/ final java.util.Date mCreatedAt; /*package*/ final java.util.Date mCreatedAt;
...@@ -46,6 +46,34 @@ public class DateRecord implements Comparable<DateRecord> { ...@@ -46,6 +46,34 @@ public class DateRecord implements Comparable<DateRecord> {
} }
public static final android.os.Parcelable.Creator<DateRecord> CREATOR
= new android.os.Parcelable.Creator<DateRecord>() {
@Override
public DateRecord createFromParcel(android.os.Parcel in) {
return new DateRecord(in);
}
@Override
public DateRecord[] newArray(int size) {
return new DateRecord[size];
}
};
public DateRecord(android.os.Parcel in) {
this.mCreatedAt = new java.util.Date(in.readLong());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(android.os.Parcel out, int flags) {
out.writeLong(mCreatedAt.getTime());
}
@Override @Override
public int compareTo(@Nonnull DateRecord other) { public int compareTo(@Nonnull DateRecord other) {
int tempResult; int tempResult;
......
...@@ -9,7 +9,7 @@ import java.util.HashSet; ...@@ -9,7 +9,7 @@ import java.util.HashSet;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class EnumUsageRecord { public class EnumUsageRecord implements android.os.Parcelable {
/*package*/ final Color mE; /*package*/ final Color mE;
...@@ -71,4 +71,53 @@ public class EnumUsageRecord { ...@@ -71,4 +71,53 @@ public class EnumUsageRecord {
"}"; "}";
} }
public static final android.os.Parcelable.Creator<EnumUsageRecord> CREATOR
= new android.os.Parcelable.Creator<EnumUsageRecord>() {
@Override
public EnumUsageRecord createFromParcel(android.os.Parcel in) {
return new EnumUsageRecord(in);
}
@Override
public EnumUsageRecord[] newArray(int size) {
return new EnumUsageRecord[size];
}
};
public EnumUsageRecord(android.os.Parcel in) {
this.mE = Color.values()[in.readInt()];
if (in.readByte() == 0) {
this.mO = null;
} else {
this.mO = Color.values()[in.readInt()];
}
this.mL = new ArrayList<Color>();
in.readList(this.mL, getClass().getClassLoader());
ArrayList<Color> mSTemp = new ArrayList<Color>();
in.readList(mSTemp, getClass().getClassLoader());
this.mS = new HashSet<Color>(mSTemp);
this.mM = new HashMap<Color, Color>();
in.readMap(this.mM, getClass().getClassLoader());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(android.os.Parcel out, int flags) {
out.writeInt(this.mE.ordinal());
if (this.mO != null) {
out.writeByte((byte)1);
out.writeInt(this.mO.ordinal());
} else {
out.writeByte((byte)0);
}
out.writeList(this.mL);
out.writeList(new ArrayList<Color>(this.mS));
out.writeMap(this.mM);
}
} }
...@@ -8,7 +8,7 @@ import java.util.HashSet; ...@@ -8,7 +8,7 @@ import java.util.HashSet;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class NestedCollection { public class NestedCollection implements android.os.Parcelable {
/*package*/ final ArrayList<HashSet<String>> mSetList; /*package*/ final ArrayList<HashSet<String>> mSetList;
...@@ -30,4 +30,33 @@ public class NestedCollection { ...@@ -30,4 +30,33 @@ public class NestedCollection {
"}"; "}";
} }
public static final android.os.Parcelable.Creator<NestedCollection> CREATOR
= new android.os.Parcelable.Creator<NestedCollection>() {
@Override
public NestedCollection createFromParcel(android.os.Parcel in) {
return new NestedCollection(in);
}
@Override
public NestedCollection[] newArray(int size) {
return new NestedCollection[size];
}
};
public NestedCollection(android.os.Parcel in) {
this.mSetList = new ArrayList<HashSet<String>>();
in.readList(this.mSetList, getClass().getClassLoader());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(android.os.Parcel out, int flags) {
out.writeList(this.mSetList);
}
} }
/*
* Mock replacement for Android's implementation of android.os.Parcel
* Used in tests to check the generation of the records that implement the parcelable interface
*/
package android.os;
import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class Parcel {
private ByteArrayOutputStream mOutStream;
private ObjectOutputStream mOut;
private ObjectInputStream mIn;
public Parcel() {
try {
mOutStream = new ByteArrayOutputStream();
mOut = new ObjectOutputStream(mOutStream);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void flush() {
try {
mOut.flush();
mIn = new ObjectInputStream(new ByteArrayInputStream(
mOutStream.toByteArray()));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void writeString(String val) {
try {
mOut.writeUTF(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final String readString() {
try {
return mIn.readUTF();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public final void writeInt(int val) {
try {
mOut.writeInt(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final int readInt() {
try {
return mIn.readInt();
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
public final void writeLong(long val) {
try {
mOut.writeLong(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final long readLong() {
try {
return mIn.readLong();
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
public final void writeFloat(float val) {
try {
mOut.writeFloat(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final float readFloat() {
try {
return mIn.readFloat();
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
public final void writeDouble(double val) {
try {
mOut.writeDouble(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final double readDouble() {
try {
return mIn.readDouble();
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
public final void writeByte(byte val) {
try {
mOut.writeByte(val);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final byte readByte() {
try {
return mIn.readByte();
} catch (Exception ex) {
ex.printStackTrace();
return 0;
}
}
public final void writeSerializable(Serializable s) {
try {
mOut.writeObject(s);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final Serializable readSerializable() {
try {
return (Serializable)mIn.readObject();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public final void writeList(List val) {
try {
mOut.writeInt(val.size());
for(Object obj : val)
mOut.writeObject(obj);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void readList(List outVal, ClassLoader loader) {
try {
int size = mIn.readInt();
for(int i = 0; i < size; ++i)
outVal.add(mIn.readObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void writeMap(Map val) {
try {
Set<Map.Entry<Object,Object>> entries = val.entrySet();
mOut.writeInt(entries.size());
for(Map.Entry<Object, Object> obj : entries) {
mOut.writeObject(obj.getKey());
mOut.writeObject(obj.getValue());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void readMap(Map outVal, ClassLoader loader) {
try {
int size = mIn.readInt();
for(int i = 0; i < size; ++i) {
Object key = mIn.readObject();
Object value = mIn.readObject();
outVal.put(key, value);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Interface for classes whose instances can be written to
* and restored from a {@link Parcel}. Classes implementing the Parcelable
* interface must also have a non-null static field called <code>CREATOR</code>
* of a type that implements the {@link Parcelable.Creator} interface.
*
* <p>A typical implementation of Parcelable is:</p>
*
* <pre>
* public class MyParcelable implements Parcelable {
* private int mData;
*
* public int describeContents() {
* return 0;
* }
*
* public void writeToParcel(Parcel out, int flags) {
* out.writeInt(mData);
* }
*
* public static final Parcelable.Creator&lt;MyParcelable&gt; CREATOR
* = new Parcelable.Creator&lt;MyParcelable&gt;() {
* public MyParcelable createFromParcel(Parcel in) {
* return new MyParcelable(in);
* }
*
* public MyParcelable[] newArray(int size) {
* return new MyParcelable[size];
* }
* };
*
* private MyParcelable(Parcel in) {
* mData = in.readInt();
* }
* }</pre>
*/
public interface Parcelable {
/**
* Flag for use with {@link #writeToParcel}: the object being written
* is a return value, that is the result of a function such as
* "<code>Parcelable someFunction()</code>",
* "<code>void someFunction(out Parcelable)</code>", or
* "<code>void someFunction(inout Parcelable)</code>". Some implementations
* may want to release resources at this point.
*/
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
/**
* Bit masks for use with {@link #describeContents}: each bit represents a
* kind of object considered to have potential special significance when
* marshalled.
*/
public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
/**
* Describe the kinds of special objects contained in this Parcelable's
* marshalled representation.
*
* @return a bitmask indicating the set of special object types marshalled
* by the Parcelable.
*/
public int describeContents();
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
public void writeToParcel(Parcel dest, int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
* field that generates instances of your Parcelable class from a Parcel.
*/
public interface Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
*
* @param source The Parcel to read the object's data from.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source);
/**
* Create a new array of the Parcelable class.
*
* @param size Size of the array.
* @return Returns an array of the Parcelable class, with every entry
* initialized to null.
*/
public T[] newArray(int size);
}
/**
* Specialization of {@link Creator} that allows you to receive the
* ClassLoader the object is being created in.
*/
public interface ClassLoaderCreator<T> extends Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
* using the given ClassLoader.
*
* @param source The Parcel to read the object's data from.
* @param loader The ClassLoader that this object is being created in.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source, ClassLoader loader);
}
}
...@@ -23,6 +23,7 @@ public class AllTests extends TestSuite { ...@@ -23,6 +23,7 @@ public class AllTests extends TestSuite {
mySuite.addTestSuite(DurationTest.class); mySuite.addTestSuite(DurationTest.class);
mySuite.addTestSuite(MockRecordTest.class); mySuite.addTestSuite(MockRecordTest.class);
mySuite.addTestSuite(WcharTest.class); mySuite.addTestSuite(WcharTest.class);
mySuite.addTestSuite(AndroidParcelableTest.class);
return mySuite; return mySuite;
} }
......
package com.dropbox.djinni.test;
import junit.framework.TestCase;
import android.os.Parcel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
public class AndroidParcelableTest extends TestCase {
public void testAssortedPrimitives() {
AssortedPrimitives p1 = new AssortedPrimitives(true, (byte)123, (short)20000, 1000000000, 1234567890123456789L, 1.23f, 1.23d,
true, (byte)123, (short)20000, 1000000000, 1234567890123456789L, 1.23f, 1.23d);
Parcel parcel = new Parcel();
p1.writeToParcel(parcel, 0);
parcel.flush();
AssortedPrimitives p2 = new AssortedPrimitives(parcel);
assertEquals(p1, p2);
}
public void testNativeCollection() {
HashSet<String> jSet1 = new HashSet<String>();
jSet1.add("String1");
jSet1.add("String2");
HashSet<String> jSet2 = new HashSet<String>();
jSet2.add("StringA");
jSet2.add("StringB");
ArrayList<HashSet<String>> jList = new ArrayList<HashSet<String>>();
jList.add(jSet1);
jList.add(jSet2);
NestedCollection c1 = new NestedCollection(jList);
Parcel parcel = new Parcel();
c1.writeToParcel(parcel, 0);
parcel.flush();
NestedCollection c2 = new NestedCollection(parcel);
assertEquals(c1.getSetList(), c2.getSetList());
}
private void performEnumTest(Color color) {
ArrayList<Color> list = new ArrayList<Color>();
list.add(null);
list.add(Color.RED);
list.add(color);
list.add(Color.ORANGE);
HashSet<Color> set = new HashSet<Color>();
set.add(color);
set.add(Color.BLUE);
HashMap<Color, Color> map = new HashMap<Color, Color>();
map.put(null, color);
map.put(Color.ORANGE, Color.RED);
Parcel parcel = new Parcel();
EnumUsageRecord r1 = new EnumUsageRecord(Color.RED, color, list, set, map);
r1.writeToParcel(parcel, 0);
parcel.flush();
EnumUsageRecord r2 = new EnumUsageRecord(parcel);
assertEquals(r1.getE(), r2.getE());
assertEquals(r1.getO(), r2.getO());
assertEquals(r1.getL(), r2.getL());
assertEquals(r1.getS(), r2.getS());
assertEquals(r1.getM(), r2.getM());
}
public void testEnum() {
performEnumTest(null);
performEnumTest(Color.ORANGE);
performEnumTest(Color.VIOLET);
}
public void testExternType() {
DateRecord dr = new DateRecord(new java.util.Date());
Parcel parcel = new Parcel();
dr.writeToParcel(parcel, 0);
parcel.flush();
DateRecord dr2 = new DateRecord(parcel);
assertEquals(dr, dr2);
}
}
...@@ -98,6 +98,7 @@ fi ...@@ -98,6 +98,7 @@ fi
--java-nullable-annotation "javax.annotation.CheckForNull" \ --java-nullable-annotation "javax.annotation.CheckForNull" \
--java-nonnull-annotation "javax.annotation.Nonnull" \ --java-nonnull-annotation "javax.annotation.Nonnull" \
--java-use-final-for-record false \ --java-use-final-for-record false \
--java-implement-android-os-parcelable true \
--ident-java-field mFooBar \ --ident-java-field mFooBar \
\ \
--cpp-out "$temp_out_relative/cpp" \ --cpp-out "$temp_out_relative/cpp" \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment