com.stanfy.helium.handler.codegen.java.entity.AndroidParcelableWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helium Show documentation
Show all versions of helium Show documentation
DSL and Java API for REST API specification
package com.stanfy.helium.handler.codegen.java.entity;
import com.squareup.javawriter.JavaWriter;
import com.stanfy.helium.handler.codegen.java.JavaPrimitiveTypes;
import com.stanfy.helium.model.Field;
import com.stanfy.helium.model.Message;
import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Writer for Android parcelables.
*/
class AndroidParcelableWriter extends DelegateJavaClassWriter {
/** Types supported by Parcel. */
private static final Map, String> SUPPORTED_TYPES_BY_ANDROID = new HashMap, String>();
static {
SUPPORTED_TYPES_BY_ANDROID.put(String.class, String.class.getSimpleName());
SUPPORTED_TYPES_BY_ANDROID.put(CharSequence.class, CharSequence.class.getSimpleName());
SUPPORTED_TYPES_BY_ANDROID.put(int.class, "Int");
SUPPORTED_TYPES_BY_ANDROID.put(long.class, "Long");
SUPPORTED_TYPES_BY_ANDROID.put(float.class, "Float");
SUPPORTED_TYPES_BY_ANDROID.put(double.class, "Double");
SUPPORTED_TYPES_BY_ANDROID.put(byte.class, "Byte");
SUPPORTED_TYPES_BY_ANDROID.put(short.class, "Short");
}
private static final String ANDROID_OS_PARCEL = "android.os.Parcel";
private static final String ANDROID_OS_PARCELABLE = "android.os.Parcelable";
private final EntitiesGeneratorOptions options;
public AndroidParcelableWriter(final JavaClassWriter core, final EntitiesGeneratorOptions options) {
super(core);
this.options = options;
}
@Override
public void writeImports(final Set imports) throws IOException {
HashSet newImports = new HashSet(imports.size() + 3);
newImports.addAll(imports);
newImports.add(ANDROID_OS_PARCELABLE);
newImports.add(ANDROID_OS_PARCEL);
super.writeImports(newImports);
}
@Override
public void writeClassBegin(final Message message, final String extending, final String... implementing) throws IOException {
String[] newImplements = new String[implementing.length + 1];
System.arraycopy(implementing, 0, newImplements, 0, implementing.length);
newImplements[newImplements.length - 1] = ANDROID_OS_PARCELABLE;
super.writeClassBegin(message, extending, newImplements);
getOutput().emitEmptyLine();
String className = message.getCanonicalName();
String creatorBody = "{\n"
+ " public " + className + " createFromParcel(Parcel source) {\n"
+ " return new " + className + "(source);\n"
+ " }\n"
+ " public " + className + "[] newArray(int size) {\n"
+ " return new " + className + "[size];\n"
+ " }\n"
+ "}";
getOutput().emitField("Creator<" + className + ">", "CREATOR",
new HashSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)),
"new Creator<" + className + ">() " + creatorBody);
}
@Override
public void writeConstructors(final Message message) throws IOException {
JavaWriter output = getOutput();
output.beginConstructor(Collections.singleton(Modifier.PUBLIC));
output.endConstructor();
output.emitEmptyLine();
output.beginConstructor(EnumSet.noneOf(Modifier.class), ANDROID_OS_PARCEL, "source");
for (Field field : message.getActiveFields()) {
emitReadingStmt(field);
}
output.endConstructor();
output.emitEmptyLine();
super.writeConstructors(message);
}
@Override
public void writeClassEnd(Message message) throws IOException {
JavaWriter output = getOutput();
output.emitEmptyLine();
output.emitAnnotation(Override.class);
output.beginMethod("int", "describeContents", EnumSet.of(Modifier.PUBLIC));
output.emitStatement("return 0");
output.endMethod();
output.emitEmptyLine();
output.emitAnnotation(Override.class);
output.beginMethod("void", "writeToParcel", EnumSet.of(Modifier.PUBLIC), ANDROID_OS_PARCEL, "dest", "int", "options");
for (Field field : message.getActiveFields()) {
emitWritingStmt(field);
}
output.endMethod();
output.emitEmptyLine();
super.writeClassEnd(message);
}
private void emitReadingStmt(final Field field) throws IOException {
String fieldName = options.getSafeFieldName(field);
JavaWriter output = getOutput();
String simpleMethod = getSupportedMethod("read", field);
if (simpleMethod != null) {
if (field.isSequence()) {
simpleMethod = simpleMethod.concat("Array").replace("read", "create");
}
output.emitStatement("this.%1$s = source.%2$s()", fieldName, simpleMethod);
return;
}
String classLoader = "getClass().getClassLoader()";
Class> clazz = getJavaClass(field);
if (clazz != null) {
// date?
if (clazz == Date.class) {
output.emitStatement("long %1$sValue = source.readLong()", fieldName);
output.emitStatement("this.%1$s = %1$sValue != -1 ? new Date(%1$sValue) : null", fieldName);
return;
}
// boolean?
if (clazz == boolean.class) {
readBoolean(field, fieldName, output);
return;
}
// enum?
if (Enum.class.isAssignableFrom(clazz)) {
String enumName = output.compressType(clazz.getCanonicalName());
output.emitStatement("this.%1$s = %2$s.values()[source.readInt()]", fieldName, enumName);
return;
}
// parcelable?
if (isAndroidParcelable(clazz)) {
readParcelable(field, fieldName, output, clazz.getCanonicalName(), classLoader);
return;
}
}
if (field.getType() instanceof Message) {
// read Parcelable
readParcelable(field, fieldName, output, field.getType().getCanonicalName(), classLoader);
return;
}
output.emitStatement("this.%1$s = (%2$s) source.readValue(%3$s)",
fieldName,
clazz != null ? clazz.getCanonicalName() : field.getType().getCanonicalName(),
classLoader);
}
private void readBoolean(Field field, String fieldName, JavaWriter output) throws IOException {
if (field.isSequence()) {
output.emitStatement("int %1$sCount = source.readInt()", fieldName);
output.beginControlFlow("if (" + fieldName + "Count > 0)");
output.emitStatement("this.%1$s = new boolean[%1$sCount]", fieldName);
output.beginControlFlow("for (int i = 0; i < " + fieldName + "Count; i++)");
output.emitStatement("this.%1$s[i] = source.readInt() == 1", fieldName);
output.endControlFlow();
output.endControlFlow();
} else {
output.emitStatement("this.%1$s = source.readInt() == 1", fieldName);
}
}
private static void readParcelable(Field field, String fieldName, JavaWriter output,
String className, String classLoader) throws IOException {
String shortClassName = output.compressType(className);
if (field.isSequence()) {
output.emitStatement("Parcelable[] %1$sParcelables = source.readParcelableArray(%2$s)",
fieldName,
classLoader);
output.beginControlFlow("if (" + fieldName + "Parcelables != null)");
output.emitStatement("this.%1$s = new %2$s[%1$sParcelables.length]", fieldName, shortClassName);
output.beginControlFlow("for (int i = 0; i < " + fieldName + "Parcelables.length; i++)");
output.emitStatement("this.%1$s[i] = (%2$s) %1$sParcelables[i]", fieldName, shortClassName);
output.endControlFlow();
output.endControlFlow();
} else {
output.emitStatement("this.%1$s = (%2$s) source.readParcelable(%3$s)",
fieldName,
shortClassName,
classLoader);
}
}
private void emitWritingStmt(final Field field) throws IOException {
String simpleMethod = getSupportedMethod("write", field);
JavaWriter output = getOutput();
String fieldName = options.getSafeFieldName(field);
if (simpleMethod != null) {
if (field.isSequence()) {
simpleMethod = simpleMethod.concat("Array");
}
output.emitStatement("dest.%s(this.%s)", simpleMethod, fieldName);
return;
}
Class> clazz = getJavaClass(field);
if (clazz != null) {
// date?
if (clazz == Date.class) {
output.emitStatement("dest.writeLong(this.%1$s != null ? this.%1$s.getTime() : -1L)", fieldName);
return;
}
// boolean?
if (clazz == boolean.class) {
writeBoolean(field, output, fieldName);
return;
}
// enum?
if (Enum.class.isAssignableFrom(clazz)) {
output.emitStatement("dest.writeInt(this.%1$s.ordinal())", fieldName);
return;
}
// parcelable?
if (isAndroidParcelable(clazz)) {
writeParcelable(output, field, fieldName);
return;
}
}
if (field.getType() instanceof Message) {
// turn it into Parcelable
writeParcelable(output, field, fieldName);
return;
}
output.emitStatement("dest.writeValue(this.%s)", fieldName);
}
private static boolean isAndroidParcelable(Class> clazz) {
for (Class> intf : clazz.getInterfaces()) {
if ("android.os.Parcelable".equals(intf.getCanonicalName())) {
return true;
}
}
return false;
}
private static void writeParcelable(JavaWriter output, Field field, String fieldName) throws IOException {
output.emitStatement("dest.writeParcelable%1$s(this.%2$s, options)",
field.isSequence() ? "Array" : "",
fieldName);
}
private static void writeBoolean(Field field, JavaWriter output, String fieldName) throws IOException {
if (field.isSequence()) {
output.emitStatement("int %1$sCount = this.%1$s != null ? this.%1$s.length : 0", fieldName);
output.emitStatement("dest.writeInt(%1$sCount)", fieldName);
output.beginControlFlow("for (int i = 0; i < " + fieldName + "Count; i++)");
output.emitStatement("dest.writeInt(this.%1$s[i] ? 1 : 0)", fieldName);
output.endControlFlow();
} else {
output.emitStatement("dest.writeInt(this.%1$s ? 1 : 0)", fieldName);
}
}
private Class> getJavaClass(final Field field) {
return field.getType().isPrimitive() ? options.getJavaClass(field.getType()) : null;
}
private String getSupportedMethod(final String prefix, final Field field) {
if (!field.getType().isPrimitive()) {
return null;
}
Class> clazz = JavaPrimitiveTypes.javaClass(field.getType());
if (clazz == null) {
return null;
}
String namePart = SUPPORTED_TYPES_BY_ANDROID.get(clazz);
return namePart != null ? prefix.concat(namePart) : null;
}
}