serialization/deserialization added

This commit is contained in:
Gavriil Sitnikov 2017-04-30 18:01:18 +03:00
parent 1f563d1a4d
commit 920cd85f2c
1 changed files with 86 additions and 40 deletions

View File

@ -38,8 +38,8 @@ import javax.lang.model.element.Modifier
//TODO: missable in future
//TODO: NUMBER/BOOLEAN enums in future
//TODO: maybe save md5-hashes to check if files/scheme changed
//TODO: move out of allvariants - too much
//TODO: serialization/deserialization
//TODO: setup generation by map yaml->package
@ -55,6 +55,8 @@ class Types {
static final TypeName API_MODEL = ClassName.bestGuess("ru.touchin.templates.ApiModel")
static final TypeName LOGAN_SQUARE_JSON_MODEL = ClassName.bestGuess("ru.touchin.templates.logansquare.LoganSquareJsonModel")
static final TypeName OBJECT_UTILS = ClassName.bestGuess("ru.touchin.roboswag.core.utils.ObjectUtils")
static final TypeName OBJECT_OUTPUT_STREAM = ClassName.bestGuess("java.io.ObjectOutputStream")
static final TypeName OBJECT_INPUT_STREAM = ClassName.bestGuess("java.io.ObjectInputStream")
}
@ -220,12 +222,12 @@ class EnumObject extends SchemeObject {
*/
enum FieldType {
BOOLEAN(TypeName.BOOLEAN, ClassName.get(Boolean.class), false),
INT(TypeName.INT, ClassName.get(Integer.class), false),
LONG(TypeName.LONG, ClassName.get(Long.class), false),
FLOAT(TypeName.FLOAT, ClassName.get(Float.class), false),
DOUBLE(TypeName.DOUBLE, ClassName.get(Double.class), false),
STRING(ClassName.get(String.class), false),
BOOLEAN(TypeName.BOOLEAN, ClassName.get(Boolean.class), false, "writeBoolean", "readBoolean"),
INT(TypeName.INT, ClassName.get(Integer.class), false, "writeInt", "readInt"),
LONG(TypeName.LONG, ClassName.get(Long.class), false, "writeLong", "readLong"),
FLOAT(TypeName.FLOAT, ClassName.get(Float.class), false, "writeFloat", "readFloat"),
DOUBLE(TypeName.DOUBLE, ClassName.get(Double.class), false, "writeDouble", "readDouble"),
STRING(ClassName.get(String.class), false, "writeUTF", "readUTF"),
LIST(ClassName.get(List.class), true),
MAP(ClassName.get(Map.class), true),
DATE_TIME(ClassName.bestGuess("org.joda.time.DateTime"), false),
@ -239,19 +241,29 @@ enum FieldType {
final TypeName nonPrimitiveTypeName
// flag to check if such type could be validate via ApiModel class methods in some way
final boolean ableToInnerValidate
final String serializationMethodName
final String deserializationMethodName
FieldType(final boolean ableToInnerValidate) {
this(null, null, ableToInnerValidate)
this(null, null, ableToInnerValidate, null, null)
}
FieldType(final TypeName typeName, final boolean ableToInnerValidate) {
this(typeName, typeName, ableToInnerValidate)
this(typeName, typeName, ableToInnerValidate, null, null)
}
FieldType(final TypeName primitiveTypeName, final TypeName nonPrimitiveTypeName, final boolean ableToInnerValidate) {
FieldType(final TypeName typeName, final boolean ableToInnerValidate,
final String serializationMethodName, final String deserializationMethodName) {
this(typeName, typeName, ableToInnerValidate, serializationMethodName, deserializationMethodName)
}
FieldType(final TypeName primitiveTypeName, final TypeName nonPrimitiveTypeName, final boolean ableToInnerValidate,
final String serializationMethodName, final String deserializationMethodName) {
this.primitiveTypeName = primitiveTypeName
this.nonPrimitiveTypeName = nonPrimitiveTypeName
this.ableToInnerValidate = ableToInnerValidate
this.serializationMethodName = serializationMethodName
this.deserializationMethodName = deserializationMethodName
}
/**
@ -679,26 +691,42 @@ class ClassObject extends SchemeObject {
.addAnnotation(AnnotationSpec.builder(Types.JSON_OBJECT).addMember("serializeNullObjects", "true").build())
.superclass(superclass != null ? superclass : Types.LOGAN_SQUARE_JSON_MODEL)
// add type variables
// adds type variables
for (String typeVariable : typeVariables) {
classBuilder.addTypeVariable(TypeVariableName.get(typeVariable))
}
// add default constructor
// adds default constructor
classBuilder.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addStatement("super()").build())
// create full constructor only if it is extends from LoganSquareJsonModel,
// creates full constructor only if it is extends from LoganSquareJsonModel,
// else we can't create constructor as parent constructor could also have parameters
final MethodSpec.Builder fullConstructorBuilder = superclass == null ?
MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addStatement("super()")
: null
// create validate() method
// creates validate() method
final MethodSpec.Builder validateMethod = MethodSpec.methodBuilder("validate").addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addException(ClassName.bestGuess("ValidationException"))
.addStatement("super.validate()")
// creates writeObject() method
final MethodSpec.Builder serializeMethod = MethodSpec.methodBuilder("writeObject").addModifiers(Modifier.PRIVATE)
.addException(ClassName.get(IOException.class))
.addParameter(ParameterSpec.builder(Types.OBJECT_OUTPUT_STREAM, "outputStream", Modifier.FINAL)
.addAnnotation(Types.NON_NULL)
.build())
// creates readObject() method
final MethodSpec.Builder deserializeMethod = MethodSpec.methodBuilder("readObject").addModifiers(Modifier.PRIVATE)
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\$S", "unchecked").build())
.addException(ClassName.get(IOException.class))
.addException(ClassName.get(ClassNotFoundException.class))
.addParameter(ParameterSpec.builder(Types.OBJECT_INPUT_STREAM, "inputStream", Modifier.FINAL)
.addAnnotation(Types.NON_NULL)
.build())
boolean first = true
final CodeBlock.Builder equalsStatement = CodeBlock.builder()
final CodeBlock.Builder hashCodeStatement = CodeBlock.builder()
@ -708,6 +736,16 @@ class ClassObject extends SchemeObject {
classBuilder.addMethod(field.generateGetterCode())
classBuilder.addMethod(field.generateSetterCode())
field.generateValidationCode(validateMethod)
final String serializeMethodName = (!field.nullable && field.type.serializationMethodName != null
? field.type.serializationMethodName : "writeObject");
final String deserializeMethodName = (!field.nullable && field.type.deserializationMethodName != null
? field.type.deserializationMethodName : "readObject");
serializeMethod.addStatement("outputStream.\$L(\$L)", serializeMethodName, field.name)
if (deserializeMethodName.equals("readObject")) {
deserializeMethod.addStatement("\$L = (\$T) inputStream.\$L()", field.name, field.typeName, deserializeMethodName)
} else{
deserializeMethod.addStatement("\$L = inputStream.\$L()", field.name, deserializeMethodName)
}
if (fullConstructorBuilder != null) {
fullConstructorBuilder.addParameter(ParameterSpec.builder(field.typeName, field.name, Modifier.FINAL)
@ -748,9 +786,9 @@ class ClassObject extends SchemeObject {
equalsStatement.add(";\n")
hashCodeStatement.add(");\n")
// create validate() method
// creates validate() method
classBuilder.addMethod(validateMethod.build())
// add equals() method
// adds equals() method
classBuilder.addMethod(MethodSpec.methodBuilder("equals").addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(TypeName.BOOLEAN)
@ -759,11 +797,15 @@ class ClassObject extends SchemeObject {
.beginControlFlow("if (object == null || getClass() != object.getClass())").addStatement("return false").endControlFlow()
.addStatement("final \$T that = (\$T) object", ClassName.bestGuess(name), ClassName.bestGuess(name))
.addCode(equalsStatement.build()).build())
// add hashCode() method
// adds hashCode() method
classBuilder.addMethod(MethodSpec.methodBuilder("hashCode").addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(TypeName.INT)
.addCode(hashCodeStatement.build()).build())
// adds writeObject() method
classBuilder.addMethod(serializeMethod.build())
// adds readObject() method
classBuilder.addMethod(deserializeMethod.build())
if (fullConstructorBuilder != null) {
classBuilder.addMethod(fullConstructorBuilder.build())
@ -791,26 +833,32 @@ class FileUtils {
android.applicationVariants.all {
variant ->
File generatedModels = new File("${project.buildDir}/generated/source/api/model/${variant.dirName}")
String modelsPackage = android.extensions.findByName("apiGeneratorModelsPackage")
String schemePath = android.extensions.findByName("apiGeneratorSchemePath")
if (modelsPackage == null) {
modelsPackage = android.defaultConfig.applicationId + '.logic.api.model'
}
final File generatedModelsDirectory = new File("${project.buildDir}/generated/source/jsonModels/${variant.dirName}")
/**
* Generating Java classes describing JSON models from specific YAML scheme.
*/
def generateJsonModelsTask = tasks.create("generateJsonModels${variant.name}") << {
String modelsPackage = android.extensions.findByName("apiGeneratorModelsPackage")
String schemeFilePath = android.extensions.findByName("apiGeneratorSchemePath")
if (modelsPackage == null) {
modelsPackage = android.defaultConfig.applicationId + '.logic.api.model'
}
if (schemePath == null) {
return
}
if (schemeFilePath == null) {
return
}
File schemeFile = new File(schemePath)
if (!schemeFile.exists()) {
schemeFile = new File("${project.projectDir}", schemePath)
}
File schemeFile = new File(schemeFilePath)
if (!schemeFile.exists()) {
schemeFile = new File("${project.projectDir}", schemeFilePath)
}
if (!schemeFile.exists()) {
throw new Exception("JSON models scheme file not found at '" + schemeFilePath + "' or at '${project.projectDir}/" + schemeFilePath + "'")
}
FileUtils.purgeDirectory(generatedModelsDirectory)
def apiModelsGenerationTask = tasks.create("apiModelsGeneration${variant.name}") << {
Yaml yaml = new Yaml()
Map<String, SchemeObject> schemeObjects = new HashMap<>()
final Yaml yaml = new Yaml()
final Map<String, SchemeObject> schemeObjects = new HashMap<>()
schemeObjects.put("Map", new ImportObject("java.util.Map"))
schemeObjects.put("List", new ImportObject("java.util.List"))
schemeObjects.put("DateTime", new ImportObject("org.joda.time.DateTime"))
@ -838,9 +886,7 @@ android.applicationVariants.all {
}
}
FileUtils.purgeDirectory(generatedModels)
for (SchemeObject schemeObject : schemeObjects.values()) {
for (final SchemeObject schemeObject : schemeObjects.values()) {
if (schemeObject instanceof ClassObject) {
try {
schemeObject.resolveFieldsInfo(schemeObjects)
@ -849,13 +895,13 @@ android.applicationVariants.all {
}
}
try {
schemeObject.writeToFile(generatedModels, schemeObjects, modelsPackage)
schemeObject.writeToFile(generatedModelsDirectory, schemeObjects, modelsPackage)
} catch (final Exception exception) {
throw new Exception("Error on generating code for '" + schemeObject.name + "' : " + exception.getMessage())
}
}
}
apiModelsGenerationTask.description = 'Generates API models'
variant.registerJavaGeneratingTask apiModelsGenerationTask, generatedModels
generateJsonModelsTask.description = 'Generates Java classes for JSON models'
variant.registerJavaGeneratingTask generateJsonModelsTask, generatedModelsDirectory
}