diff options
author | Eugene Susla <eugenesusla@google.com> | 2019-07-25 14:05:12 -0700 |
---|---|---|
committer | Eugene Susla <eugenesusla@google.com> | 2019-08-05 16:54:41 -0700 |
commit | 3156a4ce21cb4de46f84b8c7264a3dc31dd8db8b (patch) | |
tree | cf2a361a56d6f45de239da3ff5f4fa58e1d59431 /tools/codegen/src/com/android/codegen/ClassPrinter.kt | |
parent | 2eaec69928b0394b7e6979c71a707d1b2418365c (diff) |
Addresses further review comments from ag/8000041
Including:
- An API to opt out of Int/StringDefs generation on per-field basis
- A way to customize Builder
- Non-optional fields are passed in Builder constructor
- Various adjustments to SampleDataclass examples, as requested
Test: . $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/runTest.sh
Change-Id: I32d2eec52f05d505ff07779d923e4793d3036579
Diffstat (limited to 'tools/codegen/src/com/android/codegen/ClassPrinter.kt')
-rw-r--r-- | tools/codegen/src/com/android/codegen/ClassPrinter.kt | 79 |
1 files changed, 73 insertions, 6 deletions
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index 33256b787964..f1645ea9a3bb 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -1,9 +1,9 @@ package com.android.codegen +import com.github.javaparser.ast.Modifier import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration import com.github.javaparser.ast.body.TypeDeclaration -import com.github.javaparser.ast.expr.BooleanLiteralExpr -import com.github.javaparser.ast.expr.NormalAnnotationExpr +import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.type.ClassOrInterfaceType /** @@ -32,10 +32,31 @@ class ClassPrinter( val PluralOf by lazy { classRef("com.android.internal.util.DataClass.PluralOf") } val Each by lazy { classRef("com.android.internal.util.DataClass.Each") } val DataClassGenerated by lazy { classRef("com.android.internal.util.DataClass.Generated") } + val DataClassSuppressConstDefs by lazy { classRef("com.android.internal.util.DataClass.SuppressConstDefsGeneration") } + val DataClassSuppress by lazy { classRef("com.android.internal.util.DataClass.Suppress") } val GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") } val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") } + val Parcelable by lazy { classRef("android.os.Parcelable") } val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") } + init { + val fieldsWithMissingNullablity = fields.filter { field -> + !field.isPrimitive + && Modifier.TRANSIENT !in field.fieldAst.modifiers + && "@$Nullable" !in field.annotations + && "@$NonNull" !in field.annotations + } + if (fieldsWithMissingNullablity.isNotEmpty()) { + abort("Non-primitive fields must have @$Nullable or @$NonNull annotation.\n" + + "Missing nullability annotations on: " + + fieldsWithMissingNullablity.joinToString(", ") { it.name }) + } + + if (!classAst.isFinal && + classAst.extendedTypes.any { it.nameAsString == Parcelable }) { + abort("Parcelable classes must be final") + } + } /** * Optionally shortens a class reference if there's a corresponding import present @@ -54,7 +75,7 @@ class ClassPrinter( return simpleName } else { val outerClass = pkg.substringAfterLast(".", "") - if (outerClass.firstOrNull()?.isUpperCase() ?: false) { + if (outerClass.firstOrNull()?.isUpperCase() == true) { return classRef(pkg) + "." + simpleName } } @@ -89,7 +110,9 @@ class ClassPrinter( ?.toMap() ?: emptyMap() - val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, Each, UnsupportedAppUsage) + val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, UnsupportedAppUsage, + DataClassSuppressConstDefs) + val knownNonValidationAnnotations = internalAnnotations + Each + Nullable /** * @return whether the given feature is enabled @@ -109,7 +132,9 @@ class ClassPrinter( return when (this) { FeatureFlag.SETTERS -> !FeatureFlag.CONSTRUCTOR() && !FeatureFlag.BUILDER() && fields.any { !it.isFinal } - FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS) || onByDefault + FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS) + || fields.any { it.hasDefault } + || onByDefault FeatureFlag.CONSTRUCTOR -> !FeatureFlag.BUILDER() FeatureFlag.PARCELABLE -> "Parcelable" in superInterfaces FeatureFlag.AIDL -> FeatureFlag.PARCELABLE() @@ -287,6 +312,48 @@ class ClassPrinter( var BuilderClass = CANONICAL_BUILDER_CLASS var BuilderType = BuilderClass + genericArgs + val customBaseBuilderAst: ClassOrInterfaceDeclaration? by lazy { + nestedClasses.find { it.nameAsString == BASE_BUILDER_CLASS } + } + + val suppressedMembers by lazy { + getSuppressedMembers(classAst) + } + val builderSuppressedMembers by lazy { + getSuppressedMembers(customBaseBuilderAst) + } + + private fun getSuppressedMembers(clazz: ClassOrInterfaceDeclaration?): List<String> { + return clazz + ?.annotations + ?.find { it.nameAsString == DataClassSuppress } + ?.as_<SingleMemberAnnotationExpr>() + ?.memberValue + ?.run { + when (this) { + is ArrayInitializerExpr -> values.map { it.asLiteralStringValueExpr().value } + is StringLiteralExpr -> listOf(value) + else -> abort("Can't parse annotation arg: $this") + } + } + ?: emptyList() + } + + fun isMethodGenerationSuppressed(name: String, vararg argTypes: String): Boolean { + return name in suppressedMembers || hasMethod(name, *argTypes) + } + + fun hasMethod(name: String, vararg argTypes: String): Boolean { + return classAst.methods.any { + it.name.asString() == name && + it.parameters.map { it.type.asString() } == argTypes.toList() + } + } + + val lazyTransientFields = classAst.fields + .filter { it.isTransient && !it.isStatic } + .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } + .filter { hasMethod("lazyInit${it.NameUpperCamel}") } init { val builderFactoryOverride = classAst.methods.find { @@ -301,7 +368,7 @@ class ClassPrinter( it.nameAsString == CANONICAL_BUILDER_CLASS } if (builderExtension != null) { - BuilderClass = GENERATED_BUILDER_CLASS + BuilderClass = BASE_BUILDER_CLASS val tp = (builderExtension as ClassOrInterfaceDeclaration).typeParameters BuilderType = if (tp.isEmpty()) BuilderClass else "$BuilderClass<${tp.map { it.nameAsString }.joinToString(", ")}>" |