From 574b7e11d5f6bbc7f2947999104b3667aef0916d Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Wed, 13 Mar 2019 13:16:33 -0700 Subject: Codegen for parcelable/dataclass boilerplate This is the initial implementation of the `codegen` cli utility for in-place java boilerplate generation See DataClass and SampleDataClass for documentation/guide/examples. See tools/codegen/ for implementation and tests/Codegen/ for tests. Bug: 64221737 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: I75177cb770f1beabc87dbae9e77ce4b93ca08e7f --- tools/codegen/.gitignore | 2 + tools/codegen/Android.bp | 18 + tools/codegen/OWNERS | 1 + tools/codegen/manifest.txt | 1 + tools/codegen/src/com/android/codegen/ClassInfo.kt | 49 ++ .../src/com/android/codegen/ClassPrinter.kt | 311 ++++++++ tools/codegen/src/com/android/codegen/ConstDef.kt | 17 + .../codegen/src/com/android/codegen/FeatureFlag.kt | 27 + tools/codegen/src/com/android/codegen/FieldInfo.kt | 216 ++++++ .../codegen/src/com/android/codegen/Generators.kt | 847 +++++++++++++++++++++ .../android/codegen/InputSignaturesComputation.kt | 122 +++ tools/codegen/src/com/android/codegen/Main.kt | 199 +++++ .../src/com/android/codegen/SharedConstants.kt | 4 + tools/codegen/src/com/android/codegen/Utils.kt | 76 ++ 14 files changed, 1890 insertions(+) create mode 100755 tools/codegen/.gitignore create mode 100644 tools/codegen/Android.bp create mode 100644 tools/codegen/OWNERS create mode 100644 tools/codegen/manifest.txt create mode 100644 tools/codegen/src/com/android/codegen/ClassInfo.kt create mode 100644 tools/codegen/src/com/android/codegen/ClassPrinter.kt create mode 100644 tools/codegen/src/com/android/codegen/ConstDef.kt create mode 100644 tools/codegen/src/com/android/codegen/FeatureFlag.kt create mode 100644 tools/codegen/src/com/android/codegen/FieldInfo.kt create mode 100644 tools/codegen/src/com/android/codegen/Generators.kt create mode 100644 tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt create mode 100755 tools/codegen/src/com/android/codegen/Main.kt create mode 100644 tools/codegen/src/com/android/codegen/SharedConstants.kt create mode 100644 tools/codegen/src/com/android/codegen/Utils.kt (limited to 'tools/codegen') diff --git a/tools/codegen/.gitignore b/tools/codegen/.gitignore new file mode 100755 index 000000000000..9fb18b42668f --- /dev/null +++ b/tools/codegen/.gitignore @@ -0,0 +1,2 @@ +.idea +out diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp new file mode 100644 index 000000000000..805b2968bca0 --- /dev/null +++ b/tools/codegen/Android.bp @@ -0,0 +1,18 @@ +java_binary_host { + name: "codegen", + manifest: "manifest.txt", + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "javaparser", + ], +} + +java_library_host { + name: "codegen-version-info", + + srcs: [ + "src/**/SharedConstants.kt", + ], +} diff --git a/tools/codegen/OWNERS b/tools/codegen/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/tools/codegen/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com \ No newline at end of file diff --git a/tools/codegen/manifest.txt b/tools/codegen/manifest.txt new file mode 100644 index 000000000000..6e1018ba6b55 --- /dev/null +++ b/tools/codegen/manifest.txt @@ -0,0 +1 @@ +Main-class: com.android.codegen.MainKt diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt new file mode 100644 index 000000000000..7ee79f651274 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -0,0 +1,49 @@ +package com.android.codegen + +import com.github.javaparser.JavaParser +import com.github.javaparser.ParseProblemException +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration + +open class ClassInfo(val sourceLines: List) { + + private val userSourceCode = (sourceLines + "}").joinToString("\n") + val fileAst = try { + JavaParser.parse(userSourceCode)!! + } catch (e: ParseProblemException) { + throw RuntimeException("Failed to parse code:\n" + + userSourceCode + .lines() + .mapIndexed { lnNum, ln -> "/*$lnNum*/$ln" } + .joinToString("\n"), + e) + } + val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration + + 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 superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration) + .implementedTypes.map { it.asString() } + + val ClassName = classAst.nameAsString + private val genericArgsAst = classAst.typeParameters + val genericArgs = if (genericArgsAst.isEmpty()) "" else { + genericArgsAst.map { it.nameAsString }.joinToString(", ").let { "<$it>" } + } + val ClassType = ClassName + genericArgs + + val constDefs = mutableListOf() + + val fields = classAst.fields + .filterNot { it.isTransient || it.isStatic } + .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } + .apply { lastOrNull()?.isLast = true } + val lazyTransientFields = classAst.fields + .filter { it.isTransient && !it.isStatic } + .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } + .filter { hasMethod("lazyInit${it.NameUpperCamel}") } +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt new file mode 100644 index 000000000000..33256b787964 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -0,0 +1,311 @@ +package com.android.codegen + +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.type.ClassOrInterfaceType + +/** + * [ClassInfo] + utilities for printing out new class code with proper indentation and imports + */ +class ClassPrinter( + source: List, + private val stringBuilder: StringBuilder, + var cliArgs: Array +) : ClassInfo(source) { + + val GENERATED_MEMBER_HEADER by lazy { "@$GeneratedMember" } + + // Imports + val NonNull by lazy { classRef("android.annotation.NonNull") } + val NonEmpty by lazy { classRef("android.annotation.NonEmpty") } + val Nullable by lazy { classRef("android.annotation.Nullable") } + val TextUtils by lazy { classRef("android.text.TextUtils") } + val LinkedHashMap by lazy { classRef("java.util.LinkedHashMap") } + val Collections by lazy { classRef("java.util.Collections") } + val Preconditions by lazy { classRef("com.android.internal.util.Preconditions") } + val ArrayList by lazy { classRef("java.util.ArrayList") } + val DataClass by lazy { classRef("com.android.internal.util.DataClass") } + val DataClassEnum by lazy { classRef("com.android.internal.util.DataClass.Enum") } + val ParcelWith by lazy { classRef("com.android.internal.util.DataClass.ParcelWith") } + 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 GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") } + val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") } + val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") } + + + /** + * Optionally shortens a class reference if there's a corresponding import present + */ + fun classRef(fullName: String): String { + if (cliArgs.contains(FLAG_NO_FULL_QUALIFIERS)) { + return fullName.split(".").dropWhile { it[0].isLowerCase() }.joinToString(".") + } + + val pkg = fullName.substringBeforeLast(".") + val simpleName = fullName.substringAfterLast(".") + if (fileAst.imports.any { imprt -> + imprt.nameAsString == fullName + || (imprt.isAsterisk && imprt.nameAsString == pkg) + }) { + return simpleName + } else { + val outerClass = pkg.substringAfterLast(".", "") + if (outerClass.firstOrNull()?.isUpperCase() ?: false) { + return classRef(pkg) + "." + simpleName + } + } + return fullName + } + + /** @see classRef */ + inline fun classRef(): String { + return classRef(T::class.java.name) + } + + /** @see classRef */ + fun memberRef(fullName: String): String { + val className = fullName.substringBeforeLast(".") + val methodName = fullName.substringAfterLast(".") + return if (fileAst.imports.any { + it.isStatic + && (it.nameAsString == fullName + || (it.isAsterisk && it.nameAsString == className)) + }) { + className.substringAfterLast(".") + "." + methodName + } else { + classRef(className) + "." + methodName + } + } + + val dataClassAnnotationFeatures = classAst.annotations + .find { it.nameAsString == DataClass } + ?.let { it as? NormalAnnotationExpr } + ?.pairs + ?.map { pair -> pair.nameAsString to (pair.value as BooleanLiteralExpr).value } + ?.toMap() + ?: emptyMap() + + val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, Each, UnsupportedAppUsage) + + /** + * @return whether the given feature is enabled + */ + operator fun FeatureFlag.invoke(): Boolean { + if (cliArgs.contains("--no-$kebabCase")) return false + if (cliArgs.contains("--$kebabCase")) return true + + val annotationKey = "gen$upperCamelCase" + if (dataClassAnnotationFeatures.containsKey(annotationKey)) { + return dataClassAnnotationFeatures[annotationKey]!! + } + + if (cliArgs.contains("--all")) return true + if (hidden) return true + + return when (this) { + FeatureFlag.SETTERS -> + !FeatureFlag.CONSTRUCTOR() && !FeatureFlag.BUILDER() && fields.any { !it.isFinal } + FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS) || onByDefault + FeatureFlag.CONSTRUCTOR -> !FeatureFlag.BUILDER() + FeatureFlag.PARCELABLE -> "Parcelable" in superInterfaces + FeatureFlag.AIDL -> FeatureFlag.PARCELABLE() + FeatureFlag.IMPLICIT_NONNULL -> fields.any { it.isNullable } + && fields.none { "@$NonNull" in it.annotations } + else -> onByDefault + } + } + + val FeatureFlag.hidden + get(): Boolean = when { + cliArgs.contains("--hidden-$kebabCase") -> true + this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden + else -> false + } + + var currentIndent = INDENT_SINGLE + private set + + fun pushIndent() { + currentIndent += INDENT_SINGLE + } + + fun popIndent() { + currentIndent = currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length) + } + + fun backspace() = stringBuilder.setLength(stringBuilder.length - 1) + val lastChar get() = stringBuilder[stringBuilder.length - 1] + + private fun appendRaw(s: String) { + stringBuilder.append(s) + } + + fun append(s: String) { + if (s.isBlank() && s != "\n") { + appendRaw(s) + } else { + appendRaw(s.lines().map { line -> + if (line.startsWith(" *")) line else line.trimStart() + }.joinToString("\n$currentIndent")) + } + } + + fun appendSameLine(s: String) { + while (lastChar.isWhitespace() || lastChar.isNewline()) { + backspace() + } + appendRaw(s) + } + + fun rmEmptyLine() { + while (lastChar.isWhitespaceNonNewline()) backspace() + if (lastChar.isNewline()) backspace() + } + + /** + * Syntactic sugar for: + * ``` + * +"code()"; + * ``` + * to append the given string plus a newline + */ + operator fun String.unaryPlus() = append("$this\n") + + /** + * Syntactic sugar for: + * ``` + * !"code()"; + * ``` + * to append the given string without a newline + */ + operator fun String.not() = append(this) + + /** + * Syntactic sugar for: + * ``` + * ... { + * ... + * }+";" + * ``` + * to append a ';' on same line after a block, and a newline afterwards + */ + operator fun Unit.plus(s: String) { + appendSameLine(s) + +"" + } + + /** + * A multi-purpose syntactic sugar for appending the given string plus anything generated in + * the given [block], the latter with the appropriate deeper indent, + * and resetting the indent back to original at the end + * + * Usage examples: + * + * ``` + * "if (...)" { + * ... + * } + * ``` + * to append a corresponding if block appropriate indentation + * + * ``` + * "void foo(...)" { + * ... + * } + * ``` + * similar to the previous one, plus an extra empty line after the function body + * + * ``` + * "void foo(" { + * + * } + * ``` + * to use proper indentation for args code and close the bracket on same line at end + * + * ``` + * "..." { + * ... + * } + * to use the correct indentation for inner code, resetting it at the end + */ + inline operator fun String.invoke(block: ClassPrinter.() -> Unit) { + if (this == " {") { + appendSameLine(this) + } else { + append(this) + } + when { + endsWith("(") -> { + indentedBy(2, block) + appendSameLine(")") + } + endsWith("{") || endsWith(")") -> { + if (!endsWith("{")) appendSameLine(" {") + indentedBy(1, block) + +"}" + if ((endsWith(") {") || endsWith(")") || this == " {") + && !startsWith("synchronized") + && !startsWith("switch") + && !startsWith("if ") + && !contains(" else ") + && !contains("new ") + && !contains("return ")) { + +"" // extra line after function definitions + } + } + else -> indentedBy(2, block) + } + } + + inline fun indentedBy(level: Int, block: ClassPrinter.() -> Unit) { + append("\n") + level times { + append(INDENT_SINGLE) + pushIndent() + } + block() + level times { popIndent() } + rmEmptyLine() + +"" + } + + inline fun Iterable.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { + forEachApply { + b() + if (isLast) { + while (lastChar == ' ' || lastChar == '\n') backspace() + if (lastChar == ',') backspace() + } + } + } + + inline operator fun invoke(f: ClassPrinter.() -> R): R = run(f) + + var BuilderClass = CANONICAL_BUILDER_CLASS + var BuilderType = BuilderClass + genericArgs + + init { + val builderFactoryOverride = classAst.methods.find { + it.isStatic && it.nameAsString == "builder" + } + if (builderFactoryOverride != null) { + BuilderClass = (builderFactoryOverride.type as ClassOrInterfaceType).nameAsString + BuilderType = builderFactoryOverride.type.asString() + } else { + val builderExtension = (fileAst.types + + classAst.childNodes.filterIsInstance(TypeDeclaration::class.java)).find { + it.nameAsString == CANONICAL_BUILDER_CLASS + } + if (builderExtension != null) { + BuilderClass = GENERATED_BUILDER_CLASS + val tp = (builderExtension as ClassOrInterfaceDeclaration).typeParameters + BuilderType = if (tp.isEmpty()) BuilderClass + else "$BuilderClass<${tp.map { it.nameAsString }.joinToString(", ")}>" + } + } + } +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/ConstDef.kt b/tools/codegen/src/com/android/codegen/ConstDef.kt new file mode 100644 index 000000000000..f559d6f87027 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ConstDef.kt @@ -0,0 +1,17 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.FieldDeclaration + +/** + * `@IntDef` or `@StringDef` + */ +data class ConstDef(val type: Type, val AnnotationName: String, val values: List) { + + enum class Type { + INT, INT_FLAGS, STRING; + + val isInt get() = this == INT || this == INT_FLAGS + } + + val CONST_NAMES get() = values.flatMap { it.variables }.map { it.nameAsString } +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/FeatureFlag.kt b/tools/codegen/src/com/android/codegen/FeatureFlag.kt new file mode 100644 index 000000000000..24150d637a7b --- /dev/null +++ b/tools/codegen/src/com/android/codegen/FeatureFlag.kt @@ -0,0 +1,27 @@ +package com.android.codegen + + +/** + * See also [ClassPrinter.invoke] for more default flag values resolution rules + */ +enum class FeatureFlag(val onByDefault: Boolean, val desc: String = "") { + PARCELABLE(false, "implement Parcelable contract"), + AIDL(false, "generate a 'parcelable declaration' .aidl file alongside"), + CONSTRUCTOR(true, "an all-argument constructor"), + BUILDER(false, "e.g. MyClass.builder().setFoo(..).build();"), + GETTERS(true, "getters, e.g. getFoo()"), + SETTERS(false, "chainable/fluent setters, e.g. setFoo(..).setBar(..)"), + WITHERS(false, "'immutable setters' returning a new instance, " + + "e.g. newFoo = foo.withBar(barValue)"), + EQUALS_HASH_CODE(false, "equals + hashCode based on fields"), + TO_STRING(false, "toString based on fields"), + BUILD_UPON(false, "builder factory from existing instance, " + + "e.g. instance.buildUpon().setFoo(..).build()"), + IMPLICIT_NONNULL(true, "treat lack of @Nullable as @NonNull for Object fields"), + COPY_CONSTRUCTOR(false, "a constructor for an instance identical to the given one"), + CONST_DEFS(true, "@Int/StringDef's based on declared static constants"), + FOR_EACH_FIELD(false, "forEachField((name, value) -> ...)"); + + val kebabCase = name.toLowerCase().replace("_", "-") + val upperCamelCase = name.split("_").map { it.toLowerCase().capitalize() }.joinToString("") +} diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt new file mode 100644 index 000000000000..f326fd5601fe --- /dev/null +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -0,0 +1,216 @@ +package com.android.codegen + +import com.github.javaparser.JavaParser +import com.github.javaparser.ast.body.FieldDeclaration +import com.github.javaparser.ast.expr.ClassExpr +import com.github.javaparser.ast.expr.Name +import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import com.github.javaparser.ast.expr.StringLiteralExpr +import com.github.javaparser.ast.type.ArrayType +import com.github.javaparser.ast.type.ClassOrInterfaceType +import com.github.javaparser.javadoc.Javadoc +import java.lang.Long + +data class FieldInfo( + val index: Int, + val fieldAst: FieldDeclaration, + private val classInfo: ClassInfo +) { + + val classPrinter = classInfo as ClassPrinter + + // AST + internal val variableAst = fieldAst.variables[0] + val typeAst = variableAst.type + + // Field type + val Type = typeAst.asString() + val FieldClass = Type.takeWhile { it != '<' } + val isPrimitive = Type in PRIMITIVE_TYPES + + // Javadoc + val javadoc: Javadoc? = fieldAst.javadoc.orElse(null) + private val javadocText = javadoc?.toText()?.let { + // Workaround for a bug in Javaparser for javadocs starting with { + if (it.hasUnbalancedCurlyBrace()) "{$it" else it + } + val javadocTextNoAnnotationLines = javadocText + ?.lines() + ?.dropLastWhile { it.startsWith("@") || it.isBlank() } + ?.let { if (it.isEmpty()) null else it } + val javadocFull = javadocText + ?.trimBlankLines() + ?.mapLines { " * $this" } + ?.let { "/**\n$it\n */" } + + + // Field name + val name = variableAst.name.asString()!! + private val isNameHungarian = name[0] == 'm' && name[1].isUpperCase() + val NameUpperCamel = if (isNameHungarian) name.substring(1) else name.capitalize() + val nameLowerCamel = if (isNameHungarian) NameUpperCamel.decapitalize() else name + val _name = if (name != nameLowerCamel) nameLowerCamel else "_$nameLowerCamel" + val SingularNameOrNull by lazy { + classPrinter { + fieldAst.annotations + .find { it.nameAsString == PluralOf } + ?.let { it as? SingleMemberAnnotationExpr } + ?.memberValue + ?.let { it as? StringLiteralExpr } + ?.value + ?.toLowerCamel() + ?.capitalize() + } + } + val SingularName by lazy { SingularNameOrNull ?: NameUpperCamel } + + + // Field value + val mayBeNull: Boolean + get() = when { + isPrimitive -> false + "@${classPrinter.NonNull}" in annotations -> false + "@${classPrinter.NonEmpty}" in annotations -> false + isNullable -> true + lazyInitializer != null -> true + else -> classPrinter { !FeatureFlag.IMPLICIT_NONNULL() } + } + val lazyInitializer + get() = classInfo.classAst.methods.find { method -> + method.nameAsString == "lazyInit$NameUpperCamel" && method.parameters.isEmpty() + }?.nameAsString + val internalGetter get() = if (lazyInitializer != null) "get$NameUpperCamel()" else name + val defaultExpr: Any? + get() { + variableAst.initializer.orElse(null)?.let { return it } + classInfo.classAst.methods.find { + it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty() + }?.run { "$nameAsString()" }?.let { return it } + if (FieldClass == "List") return "${classPrinter.memberRef("java.util.Collections.emptyList")}()" + return null + } + val hasDefault get() = defaultExpr != null + + + // Generic args + val isArray = Type.endsWith("[]") + val isList = FieldClass == "List" || FieldClass == "ArrayList" + val fieldBit = "0x${Long.toHexString(1L shl index)}" + var isLast = false + val isFinal = fieldAst.isFinal + val fieldTypeGenegicArgs = when (typeAst) { + is ArrayType -> listOf(fieldAst.elementType.asString()) + is ClassOrInterfaceType -> { + typeAst.typeArguments.orElse(null)?.map { it.asString() } ?: emptyList() + } + else -> emptyList() + } + val FieldInnerType = fieldTypeGenegicArgs.firstOrNull() + val FieldInnerClass = FieldInnerType?.takeWhile { it != '<' } + + + // Annotations + var intOrStringDef = null as ConstDef? + val annotations by lazy { + if (FieldClass in BUILTIN_SPECIAL_PARCELLINGS) { + classPrinter { + fieldAst.addAnnotation(SingleMemberAnnotationExpr( + Name(ParcelWith), + ClassExpr(JavaParser.parseClassOrInterfaceType( + "$Parcelling.BuiltIn.For$FieldClass")))) + } + } + fieldAst.annotations.map { it.removeComment().toString() } + } + val annotationsNoInternal by lazy { + annotations.filterNot { ann -> + classPrinter { + internalAnnotations.any { + it in ann + } + } + } + } + + fun hasAnnotation(a: String) = annotations.any { it.startsWith(a) } + val isNullable by lazy { hasAnnotation("@Nullable") } + val isNonEmpty by lazy { hasAnnotation("@${classPrinter.NonEmpty}") } + val customParcellingClass by lazy { + fieldAst.annotations.find { it.nameAsString == classPrinter.ParcelWith } + ?.singleArgAs() + ?.type + ?.asString() + } + val annotationsAndType by lazy { (annotationsNoInternal + Type).joinToString(" ") } + val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } } + val annotatedTypeForSetterParam by lazy { + (annotationsNoInternal + if (isArray) "$FieldInnerType..." else Type).joinToString(" ") + } + + // Utilities + + /** + * `mFoo.size()` + */ + val ClassPrinter.sizeExpr get() = when { + isArray && FieldInnerClass !in PRIMITIVE_TYPES -> + memberRef("com.android.internal.util.ArrayUtils.size") + "($name)" + isArray -> "$name.length" + listOf("List", "Set", "Map").any { FieldClass.endsWith(it) } -> + memberRef("com.android.internal.util.CollectionUtils.size") + "($name)" + Type == "String" -> memberRef("android.text.TextUtils.length") + "($name)" + Type == "CharSequence" -> "$name.length()" + else -> "$name.size()" + } + /** + * `mFoo.get(0)` + */ + fun elemAtIndexExpr(indexExpr: String) = when { + isArray -> "$name[$indexExpr]" + FieldClass == "ArraySet" -> "$name.valueAt($indexExpr)" + else -> "$name.get($indexExpr)" + } + /** + * `mFoo.isEmpty()` + */ + val ClassPrinter.isEmptyExpr get() = when { + isArray || Type == "CharSequence" -> "$sizeExpr == 0" + else -> "$name.isEmpty()" + } + + /** + * `mFoo == that` or `Objects.equals(mFoo, that)`, etc. + */ + fun ClassPrinter.isEqualToExpr(that: String) = when { + Type in PRIMITIVE_TYPES -> "$internalGetter == $that" + isArray -> "${memberRef("java.util.Arrays.equals")}($internalGetter, $that)" + else -> "${memberRef("java.util.Objects.equals")}($internalGetter, $that)" + } + + /** + * Parcel.write* and Parcel.read* method name wildcard values + */ + val ParcelMethodsSuffix = when { + FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + + listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle", + "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") -> + FieldClass + FieldClass == "Map" && fieldTypeGenegicArgs[0] == "String" -> "Map" + isArray -> when { + FieldInnerType!! in (PRIMITIVE_TYPES + "String") -> FieldInnerType + "Array" + isBinder(FieldInnerType) -> "BinderArray" + else -> "TypedArray" + } + isList -> when { + FieldInnerType == "String" -> "StringList" + isBinder(FieldInnerType!!) -> "BinderList" + else -> "ParcelableList" + } + isIInterface(Type) -> "StrongInterface" + isBinder(Type) -> "StrongBinder" + else -> "TypedObject" + }.capitalize() + + private fun isBinder(type: String) = type == "Binder" || type == "IBinder" || isIInterface(type) + private fun isIInterface(type: String) = type.length >= 2 && type[0] == 'I' && type[1].isUpperCase() +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt new file mode 100644 index 000000000000..ab64f4efc8d8 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -0,0 +1,847 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.FieldDeclaration +import com.github.javaparser.ast.body.VariableDeclarator +import com.github.javaparser.ast.expr.* +import java.io.File + + +/** + * IntDefs and StringDefs based on constants + */ +fun ClassPrinter.generateConstDefs() { + val consts = classAst.fields.filter { + it.isStatic && it.isFinal && it.variables.all { variable -> + val initializer = variable.initializer.orElse(null) + val isLiteral = initializer is LiteralExpr + || (initializer is UnaryExpr && initializer.expression is LiteralExpr) + isLiteral && variable.type.asString() in listOf("int", "String") + } + }.flatMap { field -> field.variables.map { it to field } } + val intConsts = consts.filter { it.first.type.asString() == "int" } + val strConsts = consts.filter { it.first.type.asString() == "String" } + val intGroups = intConsts.groupBy { it.first.nameAsString.split("_")[0] }.values + val strGroups = strConsts.groupBy { it.first.nameAsString.split("_")[0] }.values + intGroups.forEach { + generateConstDef(it) + } + strGroups.forEach { + generateConstDef(it) + } +} + +fun ClassPrinter.generateConstDef(consts: List>) { + if (consts.size <= 1) return + + val names = consts.map { it.first.nameAsString!! } + val prefix = names + .reduce { a, b -> a.commonPrefixWith(b) } + .dropLastWhile { it != '_' } + .dropLast(1) + if (prefix.isEmpty()) { + println("Failed to generate const def for $names") + return + } + var AnnotationName = prefix.split("_") + .filterNot { it.isBlank() } + .map { it.toLowerCase().capitalize() } + .joinToString("") + val annotatedConst = consts.find { it.second.annotations.isNonEmpty } + if (annotatedConst != null) { + AnnotationName = annotatedConst.second.annotations.first().nameAsString + } + val type = consts[0].first.type.asString() + val flag = type == "int" && consts.all { it.first.initializer.get().toString().startsWith("0x") } + val constDef = ConstDef(type = when { + type == "String" -> ConstDef.Type.STRING + flag -> ConstDef.Type.INT_FLAGS + else -> ConstDef.Type.INT + }, + AnnotationName = AnnotationName, + values = consts.map { it.second } + ) + constDefs += constDef + fields.forEachApply { + if (fieldAst.annotations.any { it.nameAsString == AnnotationName }) { + this.intOrStringDef = constDef + } + } + + val visibility = if (consts[0].second.isPublic) "public" else "/* package-*/" + + val Retention = classRef("java.lang.annotation.Retention") + val RetentionPolicySource = memberRef("java.lang.annotation.RetentionPolicy.SOURCE") + val ConstDef = classRef("android.annotation.${type.capitalize()}Def") + + "@$ConstDef(${if_(flag, "flag = true, ")}prefix = \"${prefix}_\", value = {" { + names.forEachLastAware { name, isLast -> + +"$name${if_(!isLast, ",")}" + } + } + ")" + +"@$Retention($RetentionPolicySource)" + +GENERATED_MEMBER_HEADER + +"$visibility @interface $AnnotationName {}" + +"" + + if (type == "int") { + +GENERATED_MEMBER_HEADER + val methodDefLine = "$visibility static String ${AnnotationName.decapitalize()}ToString(" + + "@$AnnotationName int value)" + if (flag) { + val flg2str = memberRef("com.android.internal.util.BitUtils.flagsToString") + methodDefLine { + "return $flg2str(" { + +"value, $ClassName::single${AnnotationName}ToString" + } + ";" + } + +GENERATED_MEMBER_HEADER + !"static String single${AnnotationName}ToString(@$AnnotationName int value)" + } else { + !methodDefLine + } + " {" { + "switch (value) {" { + names.forEach { name -> + "case $name:" { + +"return \"$name\";" + } + } + +"default: return Integer.toHexString(value);" + } + } + } +} + +fun ClassPrinter.generateAidl(javaFile: File) { + val aidl = File(javaFile.path.substringBeforeLast(".java") + ".aidl") + if (aidl.exists()) return + aidl.writeText(buildString { + sourceLines.dropLastWhile { !it.startsWith("package ") }.forEach { + appendln(it) + } + append("\nparcelable $ClassName;\n") + }) +} + +/** + * ``` + * Foo newFoo = oldFoo.withBar(newBar); + * ``` + */ +fun ClassPrinter.generateWithers() { + fields.forEachApply { + val metodName = "with$NameUpperCamel" + if (!hasMethod(metodName, Type)) { + generateFieldJavadoc(forceHide = FeatureFlag.WITHERS.hidden) + """@$NonNull + $GENERATED_MEMBER_HEADER + public $ClassType $metodName($annotatedTypeForSetterParam value)""" { + val changedFieldName = name + + "return new $ClassType(" { + fields.forEachTrimmingTrailingComma { + if (name == changedFieldName) +"value," else +"$name," + } + } + ";" + } + } + } +} + +fun ClassPrinter.generateCopyConstructor() { + if (classAst.constructors.any { + it.parameters.size == 1 && + it.parameters[0].type.asString() == ClassType + }) { + return + } + + +"/** Copy constructor */" + +GENERATED_MEMBER_HEADER + "public $ClassName(@$NonNull $ClassName orig)" { + fields.forEachApply { + +"$name = orig.$name;" + } + } +} + +/** + * ``` + * Foo newFoo = oldFoo.buildUpon().setBar(newBar).build(); + * ``` + */ +fun ClassPrinter.generateBuildUpon() { + if (hasMethod("buildUpon")) return + + +"/**" + +" * Provides an instance of {@link $BuilderClass} with state corresponding to this instance." + if (FeatureFlag.BUILD_UPON.hidden) { + +" * @hide" + } + +" */" + +GENERATED_MEMBER_HEADER + "public $BuilderType buildUpon()" { + "return new $BuilderType()" { + fields.forEachApply { + +".set$NameUpperCamel($internalGetter)" + } + ";" + } + } +} + +fun ClassPrinter.generateBuilder() { + val setterVisibility = if (cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS)) + "protected" else "public" + val constructorVisibility = if (BuilderClass == CANONICAL_BUILDER_CLASS) + "public" else "/* package-*/" + + val OneTimeUseBuilder = classRef("android.provider.OneTimeUseBuilder") + + +"/**" + +" * A builder for {@link $ClassName}" + if (FeatureFlag.BUILDER.hidden) +" * @hide" + +" */" + +"@SuppressWarnings(\"WeakerAccess\")" + +GENERATED_MEMBER_HEADER + "public static class $BuilderClass$genericArgs" { + +"extends $OneTimeUseBuilder<$ClassType>" + } + " {" { + + +"" + fields.forEachApply { + +"protected $annotationsAndType $name;" + } + +"" + +"protected long mBuilderFieldsSet = 0L;" + +"" + +"$constructorVisibility $BuilderClass() {};" + +"" + + generateBuilderSetters(setterVisibility) + + generateBuilderBuild() + + rmEmptyLine() + } +} + +private fun ClassPrinter.generateBuilderSetters(visibility: String) { + + fields.forEachApply { + val maybeCast = + if_(BuilderClass != CANONICAL_BUILDER_CLASS, " ($CANONICAL_BUILDER_CLASS)") + + generateFieldJavadoc() + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS set$NameUpperCamel($annotatedTypeForSetterParam value)" { + +"checkNotUsed();" + +"mBuilderFieldsSet |= $fieldBit;" + +"$name = value;" + +"return$maybeCast this;" + } + + + val javadocSeeSetter = "/** @see #set$NameUpperCamel */" + val singularNameCustomizationHint = if (SingularNameOrNull == null) { + "// You can refine this method's name by providing item's singular name, e.g.:\n" + + "// @DataClass.PluralOf(\"item\")) mItems = ...\n\n" + } else "" + + if (isList && FieldInnerType != null) { + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS add$SingularName(@$NonNull $FieldInnerType value)" { + !singularNameCustomizationHint + +"if ($name == null) set$NameUpperCamel(new $ArrayList<>());" + +"$name.add(value);" + +"return$maybeCast this;" + } + } + + if (Type.contains("Map<")) { + val (Key, Value) = fieldTypeGenegicArgs + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS add$SingularName($Key key, $Value value)" { + !singularNameCustomizationHint + +"if ($name == null) set$NameUpperCamel(new $LinkedHashMap());" + +"$name.put(key, value);" + +"return$maybeCast this;" + } + } + + if (Type == "boolean") { + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS mark$NameUpperCamel()" { + +"return set$NameUpperCamel(true);" + } + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS markNot$NameUpperCamel()" { + +"return set$NameUpperCamel(false);" + } + } + } +} + +private fun ClassPrinter.generateBuilderBuild() { + +"/** Builds the instance. This builder should not be touched after calling this! */" + "public $ClassType build()" { + +"markUsed();" + fields.forEachApply { + if (!isNullable || hasDefault) { + "if ((mBuilderFieldsSet & $fieldBit) == 0)" { + if (!isNullable && !hasDefault) { + +"throw new IllegalStateException(\"Required field not set: $nameLowerCamel\");" + } else { + +"$name = $defaultExpr;" + } + } + } + } + "$ClassType o = new $ClassType(" { + fields.forEachTrimmingTrailingComma { + +"$name," + } + } + ";" + +"return o;" + } +} + +fun ClassPrinter.generateParcelable() { + val booleanFields = fields.filter { it.Type == "boolean" } + val objectFields = fields.filter { it.Type !in PRIMITIVE_TYPES } + val nullableFields = objectFields.filter { it.mayBeNull } + val nonBooleanFields = fields - booleanFields + + + val flagStorageType = when (fields.size) { + in 0..7 -> "byte" + in 8..15 -> "int" + in 16..31 -> "long" + else -> throw NotImplementedError("32+ field classes not yet supported") + } + val FlagStorageType = flagStorageType.capitalize() + + fields.forEachApply { + if (sParcelling != null) { + +GENERATED_MEMBER_HEADER + "static $Parcelling<$Type> $sParcelling =" { + "$Parcelling.Cache.get(" { + +"$customParcellingClass.class" + } + ";" + } + "static {" { + "if ($sParcelling == null)" { + "$sParcelling = $Parcelling.Cache.put(" { + +"new $customParcellingClass()" + } + ";" + } + } + +"" + } + } + + val Parcel = classRef("android.os.Parcel") + if (!hasMethod("writeToParcel", Parcel, "int")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public void writeToParcel($Parcel dest, int flags)" { + +"// You can override field parcelling by defining methods like:" + +"// void parcelFieldName(Parcel dest, int flags) { ... }" + +"" + + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { + +"$flagStorageType flg = 0;" + booleanFields.forEachApply { + +"if ($internalGetter) flg |= $fieldBit;" + } + nullableFields.forEachApply { + +"if ($internalGetter != null) flg |= $fieldBit;" + } + +"dest.write$FlagStorageType(flg);" + } + + nonBooleanFields.forEachApply { + val customParcellingMethod = "parcel$NameUpperCamel" + when { + hasMethod(customParcellingMethod, Parcel, "int") -> + +"$customParcellingMethod(dest, flags);" + customParcellingClass != null -> +"$sParcelling.parcel($name, dest, flags);" + hasAnnotation("@$DataClassEnum") -> + +"dest.writeInt($internalGetter == null ? -1 : $internalGetter.ordinal());" + else -> { + if (mayBeNull) !"if ($internalGetter != null) " + var args = internalGetter + if (ParcelMethodsSuffix.startsWith("Parcelable") + || ParcelMethodsSuffix.startsWith("TypedObject") + || ParcelMethodsSuffix == "TypedArray") { + args += ", flags" + } + +"dest.write$ParcelMethodsSuffix($args);" + } + } + } + } + } + + if (!hasMethod("describeContents")) { + +"@Override" + +GENERATED_MEMBER_HEADER + +"public int describeContents() { return 0; }" + +"" + } + + if (classAst.fields.none { it.variables[0].nameAsString == "CREATOR" }) { + val Creator = classRef("android.os.Parcelable.Creator") + + +GENERATED_MEMBER_HEADER + "public static final @$NonNull $Creator<$ClassName> CREATOR" { + +"= new $Creator<$ClassName>()" + }; " {" { + + +"@Override" + "public $ClassName[] newArray(int size)" { + +"return new $ClassName[size];" + } + + +"@Override" + +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})" + "public $ClassName createFromParcel($Parcel in)" { + +"// You can override field unparcelling by defining methods like:" + +"// static FieldType unparcelFieldName(Parcel in) { ... }" + +"" + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { + +"$flagStorageType flg = in.read$FlagStorageType();" + } + booleanFields.forEachApply { + +"$Type $_name = (flg & $fieldBit) != 0;" + } + nonBooleanFields.forEachApply { + + // Handle customized parceling + val customParcellingMethod = "unparcel$NameUpperCamel" + if (hasMethod(customParcellingMethod, Parcel)) { + +"$Type $_name = $customParcellingMethod(in);" + } else if (customParcellingClass != null) { + +"$Type $_name = $sParcelling.unparcel(in);" + } else if (hasAnnotation("@$DataClassEnum")) { + val ordinal = "${_name}Ordinal" + +"int $ordinal = in.readInt();" + +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];" + } else { + val methodArgs = mutableListOf() + + // Create container if any + val containerInitExpr = when { + FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" + FieldClass == "List" || FieldClass == "ArrayList" -> + "new ${classRef("java.util.ArrayList")}<>()" +// isArray && FieldInnerType in (PRIMITIVE_TYPES + "String") -> +// "new $FieldInnerType[in.readInt()]" + else -> "" + } + val passContainer = containerInitExpr.isNotEmpty() + + // nullcheck + + // "FieldType fieldName = (FieldType)" + if (passContainer) { + methodArgs.add(_name) + !"$Type $_name = " + if (mayBeNull) { + +"null;" + !"if ((flg & $fieldBit) != 0) {" + pushIndent() + +"" + !"$_name = " + } + +"$containerInitExpr;" + } else { + !"$Type $_name = " + if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : " + if (ParcelMethodsSuffix == "StrongInterface") { + !"$FieldClass.Stub.asInterface(" + } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" && + (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") && + ParcelMethodsSuffix != "Parcelable") { + !"($Type) " + } + } + + // Determine method args + when { + ParcelMethodsSuffix == "Parcelable" -> + methodArgs += "$FieldClass.class.getClassLoader()" + ParcelMethodsSuffix == "TypedObject" -> + methodArgs += "$FieldClass.CREATOR" + ParcelMethodsSuffix == "TypedArray" -> + methodArgs += "$FieldInnerClass.CREATOR" + ParcelMethodsSuffix.startsWith("Parcelable") + || FieldClass == "Map" + || (isList || isArray) + && FieldInnerType !in PRIMITIVE_TYPES + "String" -> + methodArgs += "$FieldInnerClass.class.getClassLoader()" + } + + // ...in.readFieldType(args...); + when { + ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder" + isArray -> !"in.create$ParcelMethodsSuffix" + else -> !"in.read$ParcelMethodsSuffix" + } + !"(${methodArgs.joinToString(", ")})" + if (ParcelMethodsSuffix == "StrongInterface") !")" + +";" + + // Cleanup if passContainer + if (passContainer && mayBeNull) { + popIndent() + rmEmptyLine() + +"\n}" + } + } + } + "return new $ClassType(" { + fields.forEachTrimmingTrailingComma { + +"$_name," + } + } + ";" + } + rmEmptyLine() + } + ";" + +"" + } +} + +fun ClassPrinter.generateEqualsHashcode() { + if (!hasMethod("equals", "Object")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public boolean equals(Object o)" { + +"// You can override field equality logic by defining either of the methods like:" + +"// boolean fieldNameEquals($ClassName other) { ... }" + +"// boolean fieldNameEquals(FieldType otherValue) { ... }" + +"" + """if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + $ClassType that = ($ClassType) o; + //noinspection PointlessBooleanExpression + return true""" { + fields.forEachApply { + val sfx = if (isLast) ";" else "" + val customEquals = "${nameLowerCamel}Equals" + when { + hasMethod(customEquals, Type) -> +"&& $customEquals(that.$internalGetter)$sfx" + hasMethod(customEquals, ClassType) -> +"&& $customEquals(that)$sfx" + else -> +"&& ${isEqualToExpr("that.$internalGetter")}$sfx" + } + } + } + } + } + + if (!hasMethod("hashCode")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public int hashCode()" { + +"// You can override field hashCode logic by defining methods like:" + +"// int fieldNameHashCode() { ... }" + +"" + +"int _hash = 1;" + fields.forEachApply { + !"_hash = 31 * _hash + " + val customHashCode = "${nameLowerCamel}HashCode" + when { + hasMethod(customHashCode) -> +"$customHashCode();" + Type == "int" || Type == "byte" -> +"$internalGetter;" + Type in PRIMITIVE_TYPES -> +"${Type.capitalize()}.hashCode($internalGetter);" + isArray -> +"${memberRef("java.util.Arrays.hashCode")}($internalGetter);" + else -> +"${memberRef("java.util.Objects.hashCode")}($internalGetter);" + } + } + +"return _hash;" + } + } +} + +//TODO support IntDef flags? +fun ClassPrinter.generateToString() { + if (!hasMethod("toString")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public String toString()" { + +"// You can override field toString logic by defining methods like:" + +"// String fieldNameToString() { ... }" + +"" + "return \"$ClassName { \" +" { + fields.forEachApply { + val customToString = "${nameLowerCamel}ToString" + val expr = when { + hasMethod(customToString) -> "$customToString()" + isArray -> "${memberRef("java.util.Arrays.toString")}($internalGetter)" + intOrStringDef?.type?.isInt == true -> + "${intOrStringDef!!.AnnotationName.decapitalize()}ToString($name)" + else -> internalGetter + } + +"\"$nameLowerCamel = \" + $expr${if_(!isLast, " + \", \"")} +" + } + } + +"\" }\";" + } + } +} + +fun ClassPrinter.generateSetters() { + fields.forEachApply { + if (!hasMethod("set$NameUpperCamel", Type) + && !fieldAst.isPublic + && !isFinal) { + + generateFieldJavadoc(forceHide = FeatureFlag.SETTERS.hidden) + +GENERATED_MEMBER_HEADER + "public $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" { + generateSetFrom("value") + +"return this;" + } + } + } +} + +fun ClassPrinter.generateGetters() { + (fields + lazyTransientFields).forEachApply { + val methodPrefix = if (Type == "boolean") "is" else "get" + val methodName = methodPrefix + NameUpperCamel + + if (!hasMethod(methodName) && !fieldAst.isPublic) { + + generateFieldJavadoc(forceHide = FeatureFlag.GETTERS.hidden) + +GENERATED_MEMBER_HEADER + "public $annotationsAndType $methodName()" { + if (lazyInitializer == null) { + +"return $name;" + } else { + +"$Type $_name = $name;" + "if ($_name == null)" { + if (fieldAst.isVolatile) { + "synchronized(this)" { + +"$_name = $name;" + "if ($_name == null)" { + +"$_name = $name = $lazyInitializer();" + } + } + } else { + +"// You can mark field as volatile for thread-safe double-check init" + +"$_name = $name = $lazyInitializer();" + } + } + +"return $_name;" + } + } + } + } +} + +fun FieldInfo.generateFieldJavadoc(forceHide: Boolean = false) = classPrinter { + if (javadocFull != null || forceHide) { + var hidden = false + (javadocFull ?: "/**\n */").lines().forEach { + if (it.contains("@hide")) hidden = true + if (it.contains("*/") && forceHide && !hidden) { + if (javadocFull != null) +" *" + +" * @hide" + } + +it + } + } +} + +fun FieldInfo.generateSetFrom(source: String) = classPrinter { + !"$name = " + if (Type in PRIMITIVE_TYPES || mayBeNull) { + +"$source;" + } else if (defaultExpr != null) { + "$source != null" { + +"? $source" + +": $defaultExpr;" + } + } else { + val checkNotNull = memberRef("com.android.internal.util.Preconditions.checkNotNull") + +"$checkNotNull($source);" + } + if (isNonEmpty) { + "if ($isEmptyExpr)" { + +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");" + } + } +} + +fun ClassPrinter.generateConstructor(visibility: String = "public") { + if (visibility == "public") { + generateConstructorJavadoc() + } + +GENERATED_MEMBER_HEADER + "$visibility $ClassName(" { + fields.forEachApply { + +"$annotationsAndType $nameLowerCamel${if_(!isLast, ",")}" + } + } + " {" { + fields.forEachApply { + !"this." + generateSetFrom(nameLowerCamel) + } + + generateStateValidation() + + generateOnConstructedCallback() + } +} + +private fun ClassPrinter.generateConstructorJavadoc() { + if (fields.all { it.javadoc == null } && !FeatureFlag.CONSTRUCTOR.hidden) return + +"/**" + fields.filter { it.javadoc != null }.forEachApply { + javadocTextNoAnnotationLines?.apply { + +" * @param $nameLowerCamel" + forEach { + +" * $it" + } + } + } + if (FeatureFlag.CONSTRUCTOR.hidden) +" * @hide" + +" */" +} + +private fun ClassPrinter.generateStateValidation() { + val Size = classRef("android.annotation.Size") + val knownNonValidationAnnotations = internalAnnotations + Nullable + + val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") + fun appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) { + "$validate(" { + !"${annotation.nameAsString}.class, null, $valueToValidate" + val params = when (annotation) { + is MarkerAnnotationExpr -> emptyMap() + is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue) + is NormalAnnotationExpr -> + annotation.pairs.map { it.name.asString() to it.value }.toMap() + else -> throw IllegalStateException() + } + params.forEach { name, value -> + !",\n\"$name\", $value" + } + } + +";" + } + + fields.forEachApply { + if (intOrStringDef != null) { + if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) { + +"" + +"//noinspection PointlessBitwiseExpression" + "$Preconditions.checkFlagsArgument(" { + "$name, 0" { + intOrStringDef!!.CONST_NAMES.forEach { + +"| $it" + } + } + } + +";" + } else { + +"" + +"//noinspection PointlessBooleanExpression" + "if (true" { + intOrStringDef!!.CONST_NAMES.forEach { CONST_NAME -> + +"&& !(${isEqualToExpr(CONST_NAME)})" + } + }; rmEmptyLine(); ") {" { + "throw new ${classRef()}(" { + "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" { + + intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast -> + +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}"""" + } + } + } + +";" + } + } + } + + val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line + val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter { + it.nameAsString != Each && + it.range.orElse(null)?.begin?.line?.let { it >= eachLine } ?: false + } + + fieldAst.annotations.filterNot { + it.nameAsString == intOrStringDef?.AnnotationName + || it.nameAsString in knownNonValidationAnnotations + || it in perElementValidations + }.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) + } + + if (perElementValidations.isNotEmpty()) { + +"int ${nameLowerCamel}Size = $sizeExpr;" + "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" { + perElementValidations.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = elemAtIndexExpr("i")) + } + } + } + } +} + +private fun ClassPrinter.generateOnConstructedCallback(prefix: String = "") { + +"" + val call = "${prefix}onConstructed();" + if (hasMethod("onConstructed")) { + +call + } else { + +"// $call // You can define this method to get a callback" + } +} + +fun ClassPrinter.generateForEachField() { + val specializations = listOf("Object", "int") + val usedSpecializations = fields.map { if (it.Type in specializations) it.Type else "Object" } + val usedSpecializationsSet = usedSpecializations.toSet() + + val PerObjectFieldAction = classRef("com.android.internal.util.DataClass.PerObjectFieldAction") + + +GENERATED_MEMBER_HEADER + "void forEachField(" { + usedSpecializationsSet.toList().forEachLastAware { specType, isLast -> + val SpecType = specType.capitalize() + val ActionClass = classRef("com.android.internal.util.DataClass.Per${SpecType}FieldAction") + +"$ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}" + } + }; " {" { + usedSpecializations.forEachIndexed { i, specType -> + val SpecType = specType.capitalize() + fields[i].apply { + +"action$SpecType.accept$SpecType(this, \"$nameLowerCamel\", $name);" + } + } + } + + if (usedSpecializationsSet.size > 1) { + +"/** @deprecated May cause boxing allocations - use with caution! */" + +"@Deprecated" + +GENERATED_MEMBER_HEADER + "void forEachField($PerObjectFieldAction<$ClassType> action)" { + fields.forEachApply { + +"action.acceptObject(this, \"$nameLowerCamel\", $name);" + } + } + } +} diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt new file mode 100644 index 000000000000..d1dc88f4a773 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -0,0 +1,122 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.TypeDeclaration +import com.github.javaparser.ast.expr.* +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations +import com.github.javaparser.ast.type.ClassOrInterfaceType +import com.github.javaparser.ast.type.Type + + +fun ClassPrinter.getInputSignatures(): List { + return classAst.fields.map { fieldAst -> + buildString { + append(fieldAst.modifiers.joinToString(" ") {it.asString()}) + append(" ") + append(annotationsToString(fieldAst)) + append(" ") + append(getFullClassName(fieldAst.commonType)) + append(" ") + append(fieldAst.variables.joinToString(", ") { it.nameAsString }) + } + } + classAst.methods.map { methodAst -> + buildString { + append(methodAst.modifiers.joinToString(" ") {it.asString()}) + append(" ") + append(annotationsToString(methodAst)) + append(" ") + append(getFullClassName(methodAst.type)) + append(" ") + append(methodAst.nameAsString) + append("(") + append(methodAst.parameters.joinToString(",") {getFullClassName(it.type)}) + append(")") + } + } +} + +private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String { + return annotatedAst.annotations.joinToString(" ") { + annotationToString(it) + } +} + +private fun ClassPrinter.annotationToString(ann: AnnotationExpr): String { + return buildString { + append("@") + append(getFullClassName(ann.nameAsString)) + if (ann is MarkerAnnotationExpr) return@buildString + + append("(") + + when (ann) { + is SingleMemberAnnotationExpr -> { + appendExpr(this, ann.memberValue) + } + is NormalAnnotationExpr -> { + ann.pairs.forEachLastAware { pair, isLast -> + append(pair.nameAsString) + append("=") + appendExpr(this, pair.value) + if (!isLast) append(", ") + } + } + } + + append(")") + }.replace("\"", "\\\"") +} + +private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) { + when (ex) { + is ClassExpr -> sb.append(getFullClassName(ex.typeAsString)).append(".class") + is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L") + is LongLiteralExpr -> sb.append(ex.asLong()).append("L") + is DoubleLiteralExpr -> sb.append(ex.asDouble()) + else -> sb.append(ex) + } +} + +private fun ClassPrinter.getFullClassName(type: Type): String { + return if (type is ClassOrInterfaceType) { + getFullClassName(buildString { + type.scope.ifPresent { append(it).append(".") } + type.isArrayType + append(type.nameAsString) + }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(", ") {getFullClassName(it)}}?.let { "<$it>" } ?: "") + } else getFullClassName(type.asString()) +} + +private fun ClassPrinter.getFullClassName(className: String): String { + if (className.endsWith("[]")) return getFullClassName(className.removeSuffix("[]")) + "[]" + + if (className.matches("\\.[a-z]".toRegex())) return className //qualified name + + if ("." in className) return getFullClassName(className.substringBeforeLast(".")) + "." + className.substringAfterLast(".") + + fileAst.imports.find { imp -> + imp.nameAsString.endsWith(".$className") + }?.nameAsString?.let { return it } + + val thisPackagePrefix = fileAst.packageDeclaration.map { it.nameAsString + "." }.orElse("") + val thisClassPrefix = thisPackagePrefix + classAst.nameAsString + "." + + classAst.childNodes.filterIsInstance>().find { + it.nameAsString == className + }?.let { return thisClassPrefix + it.nameAsString } + + constDefs.find { it.AnnotationName == className }?.let { return thisClassPrefix + className } + + if (tryOrNull { Class.forName("java.lang.$className") } != null) { + return "java.lang.$className" + } + + if (className[0].isLowerCase()) return className //primitive + + return thisPackagePrefix + className +} + +private inline fun tryOrNull(f: () -> T?) = try { + f() +} catch (e: Exception) { + null +} diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt new file mode 100755 index 000000000000..8fafa7ce9b1e --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -0,0 +1,199 @@ +package com.android.codegen + +import java.io.File + + +const val THIS_SCRIPT_LOCATION = "" +const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" +const val INDENT_SINGLE = " " + +val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") + +const val CANONICAL_BUILDER_CLASS = "Builder" +const val GENERATED_BUILDER_CLASS = "GeneratedBuilder" + +val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") + +const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters" +const val FLAG_NO_FULL_QUALIFIERS = "--no-full-qualifiers" + + +/** @see [FeatureFlag] */ +val USAGE = """ +Usage: $CODEGEN_NAME [--[PREFIX-]FEATURE...] JAVAFILE + +Generates boilerplade parcelable/data class code at the bottom of JAVAFILE, based o fields' declaration in the given JAVAFILE's top-level class + +FEATURE represents some generatable code, and can be among: +${FeatureFlag.values().map { feature -> + " ${feature.kebabCase}" to feature.desc +}.columnize(" - ")} + +And PREFIX can be: + - request to generate the feature + no - suppress generation of the feature + hidden - request to generate the feature with @hide + +Extra options: + --help - view this help + --update-only - auto-detect flags from the previously auto-generated comment within the file + $FLAG_NO_FULL_QUALIFIERS + - when referring to classes don't use package name prefix; handy with IDE auto-import + $FLAG_BUILDER_PROTECTED_SETTERS + - make builder's setters protected to expose them as public in a subclass on a whitelist basis + + +Special field modifiers and annotations: + transient - ignore the field completely + @Nullable - support null value when parcelling, and never throw on null input + @NonNull - throw on null input and don't parcel the nullness bit for the field + @DataClass.Enum - parcel field as an enum value by ordinal + @DataClass.PluralOf(..) - provide a singular version of a collection field name to be used in the builder's 'addFoo(..)' + @DataClass.ParcelWith(..) - provide a custom Parcelling class, specifying the custom (un)parcelling logic for this field + = ; - provide default value and never throw if this field was not provided e.g. when using builder + /** ... */ - copy given javadoc on field's getters/setters/constructor params/builder setters etc. + @hide (in javadoc) - force field's getters/setters/withers/builder setters to be @hide-den if generated + + +Special methods/etc. you can define: + + + For any method to be generated, if a method with same name and argument types is already + defined, than that method will not be generated. + This allows you to override certain details on granular basis. + + void onConstructed() + Will be called in constructor, after all the fields have been initialized. + This is a good place to put any custom validation logic that you may have + + static class $CANONICAL_BUILDER_CLASS extends $GENERATED_BUILDER_CLASS + If a class extending $GENERATED_BUILDER_CLASS is specified, generated builder's setters will + return the provided $CANONICAL_BUILDER_CLASS type. + $GENERATED_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead + This allows you to extend the generated builder, adding or overriding any methods you may want + + +In addition, for any field mMyField(or myField) of type FieldType you can define the following methods: + + void parcelMyField(Parcel dest, int flags) + Allows you to provide custom logic for storing mMyField into a Parcel + + static FieldType unparcelMyField(Parcel in) + Allows you to provide custom logic to deserialize the value of mMyField from a Parcel + + String myFieldToString() + Allows you to provide a custom toString representation of mMyField's value + + FieldType lazyInitMyField() + Requests a lazy initialization in getMyField(), with the provided method being the constructor + You may additionally mark the fields as volatile to cause this to generate a thread-safe + double-check locking lazy initialization + + FieldType defaultMyField() + Allows you to provide a default value to initialize the field to, in case an explicit one + was not provided. + This is an alternative to providing a field initializer that, unlike the initializer, + you can use with final fields. + +Version: $CODEGEN_VERSION +Questions? Feedback? Contact: eugenesusla@ +""" + +fun main(args: Array) { + if (args.contains("--help")) { + println(USAGE) + System.exit(0) + } + if (args.contains("--version")) { + println(CODEGEN_VERSION) + System.exit(0) + } + val file = File(args.last()) + val sourceLinesNoClosingBrace = file.readLines().dropLastWhile { + it.startsWith("}") || it.all(Char::isWhitespace) + } + val cliArgs = handleUpdateFlag(args, sourceLinesNoClosingBrace) + val sourceLinesAsIs = discardGeneratedCode(sourceLinesNoClosingBrace) + val sourceLines = sourceLinesAsIs + .filterNot { it.trim().startsWith("//") } + .map { it.trimEnd().dropWhile { it == '\n' } } + + val stringBuilder = StringBuilder(sourceLinesAsIs.joinToString("\n")) + ClassPrinter(sourceLines, stringBuilder, cliArgs).run { + + val cliExecutable = "$THIS_SCRIPT_LOCATION$CODEGEN_NAME" + val fileEscaped = file.absolutePath.replace( + System.getenv("ANDROID_BUILD_TOP"), "\$ANDROID_BUILD_TOP") + + + +""" + + + + // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. + // on ${currentTimestamp()} + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped + // + // CHECKSTYLE:OFF Generated code + """ + + if (FeatureFlag.CONST_DEFS()) generateConstDefs() + + "@$DataClassGenerated(" { + +"time = ${System.currentTimeMillis()}L," + +"codegenVersion = \"$CODEGEN_VERSION\"," + +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\"," + +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\"" + } + +"\n" + + + if (FeatureFlag.CONSTRUCTOR()) { + generateConstructor("public") + } else if (FeatureFlag.BUILDER() + || FeatureFlag.COPY_CONSTRUCTOR() + || FeatureFlag.WITHERS() + || FeatureFlag.PARCELABLE()) { + generateConstructor("/* package-private */") + } + + if (FeatureFlag.GETTERS()) generateGetters() + if (FeatureFlag.SETTERS()) generateSetters() + if (FeatureFlag.TO_STRING()) generateToString() + if (FeatureFlag.EQUALS_HASH_CODE()) generateEqualsHashcode() + + if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField() + + if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() + if (FeatureFlag.WITHERS()) generateWithers() + + if (FeatureFlag.PARCELABLE()) generateParcelable() + + if (FeatureFlag.BUILDER() && FeatureFlag.BUILD_UPON()) generateBuildUpon() + if (FeatureFlag.BUILDER()) generateBuilder() + + if (FeatureFlag.AIDL()) generateAidl(file) + + rmEmptyLine() + } + stringBuilder.append("\n}\n") + file.writeText(stringBuilder.toString().mapLines { trimEnd() }) +} + +internal fun discardGeneratedCode(sourceLinesNoClosingBrace: List): List { + return sourceLinesNoClosingBrace + .takeWhile { GENERATED_WARNING_PREFIX !in it } + .dropLastWhile(String::isBlank) +} + +private fun handleUpdateFlag(cliArgs: Array, sourceLines: List): Array { + if ("--update-only" in cliArgs + && sourceLines.none { GENERATED_WARNING_PREFIX in it || it.startsWith("@DataClass") }) { + System.exit(0) + } + return cliArgs - "--update-only" +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt new file mode 100644 index 000000000000..41641f6dab47 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -0,0 +1,4 @@ +package com.android.codegen + +const val CODEGEN_NAME = "codegen" +const val CODEGEN_VERSION = "0.0.1" \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt new file mode 100644 index 000000000000..95c99092e2ab --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -0,0 +1,76 @@ +package com.android.codegen + +import com.github.javaparser.ast.expr.AnnotationExpr +import com.github.javaparser.ast.expr.Expression +import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle + +/** + * [Iterable.forEach] + [Any.apply] + */ +inline fun Iterable.forEachApply(block: T.() -> Unit) = forEach(block) + +inline fun String.mapLines(f: String.() -> String?) = lines().mapNotNull(f).joinToString("\n") +inline fun Iterable.trim(f: T.() -> Boolean) = dropWhile(f).dropLastWhile(f) +fun String.trimBlankLines() = lines().trim { isBlank() }.joinToString("\n") + +fun Char.isNewline() = this == '\n' || this == '\r' +fun Char.isWhitespaceNonNewline() = isWhitespace() && !isNewline() + +fun if_(cond: Boolean, then: String) = if (cond) then else "" + +inline infix fun Int.times(action: () -> Unit) { + for (i in 1..this) action() +} + +/** + * a bbb + * cccc dd + * + * -> + * + * a bbb + * cccc dd + */ +fun Iterable>.columnize(separator: String = " | "): String { + val col1w = map { (a, _) -> a.length }.max()!! + val col2w = map { (_, b) -> b.length }.max()!! + return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n") +} + +fun String.hasUnbalancedCurlyBrace(): Boolean { + var braces = 0 + forEach { + if (it == '{') braces++ + if (it == '}') braces-- + if (braces < 0) return true + } + return false +} + +fun String.toLowerCamel(): String { + if (length >= 2 && this[0] == 'm' && this[1].isUpperCase()) return substring(1).capitalize() + if (all { it.isLetterOrDigit() }) return decapitalize() + return split("[^a-zA-Z0-9]".toRegex()) + .map { it.toLowerCase().capitalize() } + .joinToString("") + .decapitalize() +} + +inline fun List.forEachLastAware(f: (T, Boolean) -> Unit) { + forEachIndexed { index, t -> f(t, index == size - 1) } +} + +@Suppress("UNCHECKED_CAST") +fun AnnotationExpr.singleArgAs() + = ((this as SingleMemberAnnotationExpr).memberValue as T) + +inline operator fun Array.minus(item: T) = toList().minus(item).toTypedArray() + +fun currentTimestamp() = DateTimeFormatter + .ofLocalizedDateTime(/* date */ FormatStyle.MEDIUM, /* time */ FormatStyle.LONG) + .withZone(ZoneId.systemDefault()) + .format(Instant.now()) \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 0fb0ffa4cc03c14828452168774198a7b3200c36 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Mon, 29 Jul 2019 13:39:48 -0700 Subject: Auto-build/update codegen Test: 1. Manually enter version/presence of build artifact, and ensure `codegen FILENAME` still works 2. run codegen again without altering version, ensure no build triggered Change-Id: I6f1dba0a476077c708bbea8ef558445107e5ad97 --- tests/Codegen/runTest.sh | 2 +- tests/Codegen/src/com/android/codegentest/SampleDataClass.java | 8 ++++---- tools/codegen/Android.bp | 2 +- tools/codegen/src/com/android/codegen/SharedConstants.kt | 2 +- .../android/processor/staledataclass/StaleDataclassProcessor.kt | 3 ++- 5 files changed, 9 insertions(+), 8 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index fe3adf9b2a70..bc1aae03e9a6 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -10,7 +10,7 @@ else return $? } - header_and_eval m -j16 codegen && \ + header_and_eval m -j16 codegen_cli && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \ cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 03127ec2814b..f69a092ed3f7 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -339,8 +339,8 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v0.0.1. - // on Jul 17, 2019, 5:10:26 PM PDT + // Code below generated by codegen v1.0.0. + // on Jul 29, 2019, 2:50:21 PM PDT // // DO NOT MODIFY! // @@ -409,8 +409,8 @@ public final class SampleDataClass implements Parcelable { public @interface StateName {} @DataClass.Generated( - time = 1563408627046L, - codegenVersion = "0.0.1", + time = 1564437021513L, + codegenVersion = "1.0.0", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.DateParcelling.class) java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) java.util.regex.Pattern mPattern\nprivate java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate boolean mActive\nprivate @com.android.codegentest.SampleDataClass.StateName java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic java.lang.CharSequence charSeq\nprivate final android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=4L) int mLimited\nprivate @android.annotation.Size(2L) @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)") diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp index 805b2968bca0..677bee2cce81 100644 --- a/tools/codegen/Android.bp +++ b/tools/codegen/Android.bp @@ -1,5 +1,5 @@ java_binary_host { - name: "codegen", + name: "codegen_cli", manifest: "manifest.txt", srcs: [ "src/**/*.kt", diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 41641f6dab47..175eea6ef0d0 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,4 +1,4 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "0.0.1" \ No newline at end of file +const val CODEGEN_VERSION = "1.0.0" \ No newline at end of file diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index 9e51180509a8..e5ec17a1d18d 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -125,6 +125,7 @@ class StaleDataclassProcessor: AbstractProcessor() { val lastGenerated = annotationParams["time"] as Long val codegenVersion = annotationParams["codegenVersion"] as String + val codegenMajorVersion = codegenVersion.substringBefore(".") val sourceRelative = File(annotationParams["sourceFile"] as String) val lastGenInputSignatures = (annotationParams["inputSignatures"] as String).lines().toSet() @@ -151,7 +152,7 @@ class StaleDataclassProcessor: AbstractProcessor() { stale += Stale(clazz, source, lastGenerated) } - if (codegenVersion != CODEGEN_VERSION) { + if (codegenMajorVersion != CODEGEN_VERSION.substringBefore(".")) { stale += Stale(clazz, source, lastGenerated) } } -- cgit v1.2.3-59-g8ed1b From 3156a4ce21cb4de46f84b8c7264a3dc31dd8db8b Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 25 Jul 2019 14:05:12 -0700 Subject: 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 --- core/java/com/android/internal/util/DataClass.java | 20 +- .../java/com/android/internal/util/Parcelling.java | 3 + tests/Codegen/runTest.sh | 1 + .../com/android/codegentest/DateParcelling.java | 51 -- .../com/android/codegentest/MyDateParcelling.java | 51 ++ .../com/android/codegentest/SampleDataClass.java | 800 ++++++++++++++------- .../android/codegentest/SampleDataClassTest.java | 18 +- .../codegentest/SampleWithCustomBuilder.java | 186 +++++ tools/codegen/src/com/android/codegen/ClassInfo.kt | 12 +- .../src/com/android/codegen/ClassPrinter.kt | 79 +- tools/codegen/src/com/android/codegen/FieldInfo.kt | 9 +- .../codegen/src/com/android/codegen/Generators.kt | 344 +++++---- .../android/codegen/InputSignaturesComputation.kt | 43 +- tools/codegen/src/com/android/codegen/Main.kt | 22 +- .../src/com/android/codegen/SharedConstants.kt | 5 +- tools/codegen/src/com/android/codegen/Utils.kt | 17 +- .../staledataclass/StaleDataclassProcessor.kt | 50 +- 17 files changed, 1185 insertions(+), 526 deletions(-) delete mode 100644 tests/Codegen/src/com/android/codegentest/DateParcelling.java create mode 100644 tests/Codegen/src/com/android/codegentest/MyDateParcelling.java create mode 100644 tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java (limited to 'tools/codegen') diff --git a/core/java/com/android/internal/util/DataClass.java b/core/java/com/android/internal/util/DataClass.java index 146f54684c02..da33f996b6a7 100644 --- a/core/java/com/android/internal/util/DataClass.java +++ b/core/java/com/android/internal/util/DataClass.java @@ -177,11 +177,10 @@ public @interface DataClass { /** * @deprecated to be used by code generator exclusively - * @hide */ @Deprecated @Retention(RetentionPolicy.SOURCE) - @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, ANNOTATION_TYPE, CONSTRUCTOR, TYPE}) + @Target({METHOD}) @interface Generated { long time(); String codegenVersion(); @@ -190,7 +189,6 @@ public @interface DataClass { /** * @deprecated to be used by code generator exclusively - * @hide */ @Deprecated @Retention(RetentionPolicy.SOURCE) @@ -198,6 +196,22 @@ public @interface DataClass { @interface Member {} } + /** + * Opt out of generating {@link #genConstDefs IntDef/StringDef}s for annotated constant + */ + @Retention(RetentionPolicy.SOURCE) + @Target({FIELD}) + @interface SuppressConstDefsGeneration {} + + /** + * A class-level annotation to suppress methods' generation by name + */ + @Retention(RetentionPolicy.SOURCE) + @Target({TYPE}) + @interface Suppress { + String[] value(); + } + /** * Callback used by {@link #genForEachField}. * diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java index 63530dc389bc..390c5969c08c 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -24,6 +24,8 @@ import java.util.regex.Pattern; /** * Describes a 2-way parcelling contract of type {@code T} into/out of a {@link Parcel} * + * Implementations should be stateless. + * * @param the type being [un]parcelled */ public interface Parcelling { @@ -69,6 +71,7 @@ public interface Parcelling { * instance or reflectively creating one. */ public static

> P getOrCreate(Class

clazz) { + // No synchronization - creating an extra instance in a race case is ok P cached = get(clazz); if (cached != null) { return cached; diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index bc1aae03e9a6..614cbb7c9eb6 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -12,6 +12,7 @@ else header_and_eval m -j16 codegen_cli && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java && \ cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/CodegenTests.apk && \ diff --git a/tests/Codegen/src/com/android/codegentest/DateParcelling.java b/tests/Codegen/src/com/android/codegentest/DateParcelling.java deleted file mode 100644 index b0b00d032553..000000000000 --- a/tests/Codegen/src/com/android/codegentest/DateParcelling.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.codegentest; - -import android.os.Parcel; - -import com.android.internal.util.Parcelling; - -import java.util.Date; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Sample {@link Parcelling} implementation for {@link Date}. - * - * See {@link SampleDataClass#mDate} for usage. - * See {@link SampleDataClass#writeToParcel} + {@link SampleDataClass#sParcellingForDate} - * for resulting generated code. - * - * Ignore {@link #sInstanceCount} - used for testing. - */ -public class DateParcelling implements Parcelling { - - static AtomicInteger sInstanceCount = new AtomicInteger(0); - - public DateParcelling() { - sInstanceCount.getAndIncrement(); - } - - @Override - public void parcel(Date item, Parcel dest, int parcelFlags) { - dest.writeLong(item.getTime()); - } - - @Override - public Date unparcel(Parcel source) { - return new Date(source.readLong()); - } -} diff --git a/tests/Codegen/src/com/android/codegentest/MyDateParcelling.java b/tests/Codegen/src/com/android/codegentest/MyDateParcelling.java new file mode 100644 index 000000000000..4faeb8ee8893 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/MyDateParcelling.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.os.Parcel; + +import com.android.internal.util.Parcelling; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Sample {@link Parcelling} implementation for {@link Date}. + * + * See {@link SampleDataClass#mDate} for usage. + * See {@link SampleDataClass#writeToParcel} + {@link SampleDataClass#sParcellingForDate} + * for resulting generated code. + * + * Ignore {@link #sInstanceCount} - used for testing. + */ +public class MyDateParcelling implements Parcelling { + + static AtomicInteger sInstanceCount = new AtomicInteger(0); + + public MyDateParcelling() { + sInstanceCount.getAndIncrement(); + } + + @Override + public void parcel(Date item, Parcel dest, int parcelFlags) { + dest.writeLong(item.getTime()); + } + + @Override + public Date unparcel(Parcel source) { + return new Date(source.readLong()); + } +} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index f69a092ed3f7..b30fde4e9c47 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -53,11 +53,13 @@ import java.util.regex.Pattern; // genAidl = true, // implied by `implements Parcelable` // genGetters = true, // on by default // genConstDefs = true, // implied by presence of constants with common prefix + genBuilder = true, // on by default if optional fields present, but suppressed by + // genConstructor + genConstructor = true, // on by default but normally suppressed by genBuilder genEqualsHashCode = true, - genBuilder = true, genToString = true, genForEachField = true, - genConstructor = true // on by default but normally suppressed by genBuilder + genSetters = true ) public final class SampleDataClass implements Parcelable { @@ -136,48 +138,53 @@ public final class SampleDataClass implements Parcelable { private int mNum4; /** - * {@link Nullable} fields are considered optional and will not throw an exception if omitted - * (or set to null) when creating an instance either via a {@link Builder} or constructor. + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. */ private @Nullable String mName; /** - * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * Fields with default value expressions ("mFoo = ...") are optional, and are automatically * initialized to the provided default expression, unless explicitly set. + * + * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter} + * while mandatory fields are passed via {@link Builder#Builder constructor}. */ - private String mName2 = "Bob"; + private @NonNull String mName2 = "Bob"; /** - * Fields without {@link Nullable} annotation or default value are considered required. - * - * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + * Alternatively, when default value computation is expensive, + * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value. */ private @NonNull String mName4; + private static String defaultName4() { + // Expensive computation + return "Bob4"; + } /** * For parcelling, any field type supported by {@link Parcel} is supported out of the box. * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. */ - private AccessibilityNodeInfo mOtherParcelable = null; + private @Nullable AccessibilityNodeInfo mOtherParcelable = null; /** * Additionally, support for parcelling other types can be added by implementing a * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. * - * @see DateParcelling an example {@link Parcelling} implementation + * @see MyDateParcelling an example {@link Parcelling} implementation */ - @DataClass.ParcelWith(DateParcelling.class) - private Date mDate = new Date(42 * 42); + @DataClass.ParcelWith(MyDateParcelling.class) + private @NonNull Date mDate = new Date(42 * 42); /** * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} * to encourage its reuse. */ @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class) - private Pattern mPattern = Pattern.compile(""); + private @NonNull Pattern mPattern = Pattern.compile(""); /** * For lists, when using a {@link Builder}, other than a regular * {@link Builder#setLinkAddresses2(List) setter}, and additional * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. */ - private List mLinkAddresses2 = new ArrayList<>(); + private @NonNull List mLinkAddresses2 = new ArrayList<>(); /** * For aesthetics, you may want to consider providing a singular version of the plural field * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. @@ -185,7 +192,7 @@ public final class SampleDataClass implements Parcelable { * @see Builder#addLinkAddress(LinkAddress) */ @DataClass.PluralOf("linkAddress") - private ArrayList mLinkAddresses = new ArrayList<>(); + private @NonNull ArrayList mLinkAddresses = new ArrayList<>(); /** * For array fields, when using a {@link Builder}, vararg argument format is used for * convenience. @@ -193,11 +200,6 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setLinkAddresses4(LinkAddress...) */ private @Nullable LinkAddress[] mLinkAddresses4 = null; - /** - * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods - * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. - */ - private boolean mActive = true; /** * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to @@ -206,7 +208,7 @@ public final class SampleDataClass implements Parcelable { * @see #getStateName * @see Builder#setStateName */ - private @StateName String mStateName = STATE_NAME_UNDEFINED; + private @StateName @NonNull String mStateName = STATE_NAME_UNDEFINED; /** * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. */ @@ -220,11 +222,11 @@ public final class SampleDataClass implements Parcelable { /** * Making a field public will suppress getter generation in favor of accessing it directly. */ - public CharSequence charSeq = ""; + public @NonNull CharSequence charSeq = ""; /** * Final fields suppress generating a setter (when setters are requested). */ - private final LinkAddress[] mLinkAddresses5; + private final @Nullable LinkAddress[] mLinkAddresses5; /** * Transient fields are completely ignored and can be used for caching. */ @@ -261,7 +263,7 @@ public final class SampleDataClass implements Parcelable { * * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) */ - private @android.annotation.IntRange(from = 0, to = 4) int mLimited = 3; + private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek = 3; /** * Unnamed validation annotation parameter gets supplied to the validating method named as * "value". @@ -272,6 +274,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @Size(2) + @NonNull @Each @FloatRange(from = 0f) private float[] mCoords = new float[] {0f, 0f}; @@ -340,7 +343,6 @@ public final class SampleDataClass implements Parcelable { // Code below generated by codegen v1.0.0. - // on Jul 29, 2019, 2:50:21 PM PDT // // DO NOT MODIFY! // @@ -408,13 +410,9 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public @interface StateName {} - @DataClass.Generated( - time = 1564437021513L, - codegenVersion = "1.0.0", - sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", - inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.DateParcelling.class) java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) java.util.regex.Pattern mPattern\nprivate java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate boolean mActive\nprivate @com.android.codegentest.SampleDataClass.StateName java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic java.lang.CharSequence charSeq\nprivate final android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=4L) int mLimited\nprivate @android.annotation.Size(2L) @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)") - -/** + /** + * Creates a new SampleDataClass. + * * @param num * Any property javadoc should go onto the field, and will be copied where appropriate, * including getters, constructor parameters, builder setters, etc. @@ -429,15 +427,16 @@ public final class SampleDataClass implements Parcelable { * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the * desired public API surface. * @param name - * {@link Nullable} fields are considered optional and will not throw an exception if omitted - * (or set to null) when creating an instance either via a {@link Builder} or constructor. + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. * @param name2 - * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * Fields with default value expressions ("mFoo = ...") are optional, and are automatically * initialized to the provided default expression, unless explicitly set. - * @param name4 - * Fields without {@link Nullable} annotation or default value are considered required. * - * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter} + * while mandatory fields are passed via {@link Builder#Builder constructor}. + * @param name4 + * Alternatively, when default value computation is expensive, + * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value. * @param otherParcelable * For parcelling, any field type supported by {@link Parcel} is supported out of the box. * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. @@ -457,9 +456,6 @@ public final class SampleDataClass implements Parcelable { * @param linkAddresses4 * For array fields, when using a {@link Builder}, vararg argument format is used for * convenience. - * @param active - * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods - * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. * @param stateName * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to * getter/constructor/setter/builder parameters, making for a nicer api. @@ -480,7 +476,7 @@ public final class SampleDataClass implements Parcelable { * You can also extend support to your custom annotations by creating another corresponding * overloads like * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. - * @param limited + * @param dayOfWeek * Validation annotations may also have parameters. * * Parameter values will be supplied to validation method as name-value pairs. @@ -497,50 +493,49 @@ public final class SampleDataClass implements Parcelable { int num2, int num4, @Nullable String name, - String name2, + @NonNull String name2, @NonNull String name4, - AccessibilityNodeInfo otherParcelable, - Date date, - Pattern pattern, - List linkAddresses2, - ArrayList linkAddresses, + @Nullable AccessibilityNodeInfo otherParcelable, + @NonNull Date date, + @NonNull Pattern pattern, + @NonNull List linkAddresses2, + @NonNull ArrayList linkAddresses, @Nullable LinkAddress[] linkAddresses4, - boolean active, - @StateName String stateName, + @StateName @NonNull String stateName, @RequestFlags int flags, @State int state, - CharSequence charSeq, - LinkAddress[] linkAddresses5, + @NonNull CharSequence charSeq, + @Nullable LinkAddress[] linkAddresses5, @StringRes int stringRes, - @android.annotation.IntRange(from = 0, to = 4) int limited, - @Size(2) @FloatRange(from = 0f) float[] coords) { + @android.annotation.IntRange(from = 0, to = 6) int dayOfWeek, + @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] coords) { this.mNum = num; this.mNum2 = num2; this.mNum4 = num4; this.mName = name; this.mName2 = name2; - this.mName4 = Preconditions.checkNotNull(name4); + AnnotationValidations.validate( + NonNull.class, null, mName2); + this.mName4 = name4; + AnnotationValidations.validate( + NonNull.class, null, mName4); this.mOtherParcelable = otherParcelable; this.mDate = date; + AnnotationValidations.validate( + NonNull.class, null, mDate); this.mPattern = pattern; + AnnotationValidations.validate( + NonNull.class, null, mPattern); this.mLinkAddresses2 = linkAddresses2; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses2); this.mLinkAddresses = linkAddresses; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses); this.mLinkAddresses4 = linkAddresses4; - this.mActive = active; this.mStateName = stateName; - this.mFlags = flags; - this.mState = state; - this.charSeq = charSeq; - this.mLinkAddresses5 = linkAddresses5; - this.mStringRes = stringRes; - this.mLimited = limited; - this.mCoords = coords; - AnnotationValidations.validate( - NonNull.class, null, mName4); - //noinspection PointlessBooleanExpression - if (true - && !(Objects.equals(mStateName, STATE_NAME_UNDEFINED)) + if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED)) && !(Objects.equals(mStateName, STATE_NAME_ON)) && !(Objects.equals(mStateName, STATE_NAME_OFF))) { throw new java.lang.IllegalArgumentException( @@ -550,17 +545,18 @@ public final class SampleDataClass implements Parcelable { + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")"); } + AnnotationValidations.validate( + NonNull.class, null, mStateName); + this.mFlags = flags; - //noinspection PointlessBitwiseExpression Preconditions.checkFlagsArgument( - mFlags, 0 - | FLAG_MANUAL_REQUEST + mFlags, + FLAG_MANUAL_REQUEST | FLAG_COMPATIBILITY_MODE_REQUEST | FLAG_AUGMENTED_REQUEST); + this.mState = state; - //noinspection PointlessBooleanExpression - if (true - && !(mState == STATE_UNDEFINED) + if (!(mState == STATE_UNDEFINED) && !(mState == STATE_ON) && !(mState == STATE_OFF)) { throw new java.lang.IllegalArgumentException( @@ -570,15 +566,24 @@ public final class SampleDataClass implements Parcelable { + "STATE_OFF(" + STATE_OFF + ")"); } + this.charSeq = charSeq; + AnnotationValidations.validate( + NonNull.class, null, charSeq); + this.mLinkAddresses5 = linkAddresses5; + this.mStringRes = stringRes; AnnotationValidations.validate( StringRes.class, null, mStringRes); + this.mDayOfWeek = dayOfWeek; AnnotationValidations.validate( - android.annotation.IntRange.class, null, mLimited, + android.annotation.IntRange.class, null, mDayOfWeek, "from", 0, - "to", 4); + "to", 6); + this.mCoords = coords; AnnotationValidations.validate( Size.class, null, mCoords.length, "value", 2); + AnnotationValidations.validate( + NonNull.class, null, mCoords); int coordsSize = mCoords.length; for (int i = 0; i < coordsSize; i++) { AnnotationValidations.validate( @@ -628,8 +633,7 @@ public final class SampleDataClass implements Parcelable { } /** - * {@link Nullable} fields are considered optional and will not throw an exception if omitted - * (or set to null) when creating an instance either via a {@link Builder} or constructor. + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. */ @DataClass.Generated.Member public @Nullable String getName() { @@ -637,18 +641,20 @@ public final class SampleDataClass implements Parcelable { } /** - * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * Fields with default value expressions ("mFoo = ...") are optional, and are automatically * initialized to the provided default expression, unless explicitly set. + * + * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter} + * while mandatory fields are passed via {@link Builder#Builder constructor}. */ @DataClass.Generated.Member - public String getName2() { + public @NonNull String getName2() { return mName2; } /** - * Fields without {@link Nullable} annotation or default value are considered required. - * - * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + * Alternatively, when default value computation is expensive, + * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value. */ @DataClass.Generated.Member public @NonNull String getName4() { @@ -660,7 +666,7 @@ public final class SampleDataClass implements Parcelable { * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. */ @DataClass.Generated.Member - public AccessibilityNodeInfo getOtherParcelable() { + public @Nullable AccessibilityNodeInfo getOtherParcelable() { return mOtherParcelable; } @@ -668,10 +674,10 @@ public final class SampleDataClass implements Parcelable { * Additionally, support for parcelling other types can be added by implementing a * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. * - * @see DateParcelling an example {@link Parcelling} implementation + * @see MyDateParcelling an example {@link Parcelling} implementation */ @DataClass.Generated.Member - public Date getDate() { + public @NonNull Date getDate() { return mDate; } @@ -680,7 +686,7 @@ public final class SampleDataClass implements Parcelable { * to encourage its reuse. */ @DataClass.Generated.Member - public Pattern getPattern() { + public @NonNull Pattern getPattern() { return mPattern; } @@ -690,7 +696,7 @@ public final class SampleDataClass implements Parcelable { * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. */ @DataClass.Generated.Member - public List getLinkAddresses2() { + public @NonNull List getLinkAddresses2() { return mLinkAddresses2; } @@ -701,19 +707,10 @@ public final class SampleDataClass implements Parcelable { * @see Builder#addLinkAddress(LinkAddress) */ @DataClass.Generated.Member - public ArrayList getLinkAddresses() { + public @NonNull ArrayList getLinkAddresses() { return mLinkAddresses; } - /** - * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods - * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. - */ - @DataClass.Generated.Member - public boolean isActive() { - return mActive; - } - /** * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to * getter/constructor/setter/builder parameters, making for a nicer api. @@ -722,7 +719,7 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setStateName */ @DataClass.Generated.Member - public @StateName String getStateName() { + public @StateName @NonNull String getStateName() { return mStateName; } @@ -746,7 +743,7 @@ public final class SampleDataClass implements Parcelable { * Final fields suppress generating a setter (when setters are requested). */ @DataClass.Generated.Member - public LinkAddress[] getLinkAddresses5() { + public @Nullable LinkAddress[] getLinkAddresses5() { return mLinkAddresses5; } @@ -775,8 +772,8 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) */ @DataClass.Generated.Member - public @android.annotation.IntRange(from = 0, to = 4) int getLimited() { - return mLimited; + public @android.annotation.IntRange(from = 0, to = 6) int getDayOfWeek() { + return mDayOfWeek; } /** @@ -789,7 +786,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @Size(2) @FloatRange(from = 0f) float[] getCoords() { + public @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] getCoords() { return mCoords; } @@ -810,6 +807,282 @@ public final class SampleDataClass implements Parcelable { return tmpStorage; } + /** + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + *

+ * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + */ + @DataClass.Generated.Member + public SampleDataClass setNum(int value) { + mNum = value; + return this; + } + + /** + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, html links, etc. + * + * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. + */ + @DataClass.Generated.Member + public SampleDataClass setNum2(int value) { + mNum2 = value; + return this; + } + + /** + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * + * @see #getNum4() is hidden + * @see Builder#setNum4(int) also hidden + * @hide + */ + @DataClass.Generated.Member + public SampleDataClass setNum4(int value) { + mNum4 = value; + return this; + } + + /** + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. + */ + @DataClass.Generated.Member + public SampleDataClass setName(@Nullable String value) { + mName = value; + return this; + } + + /** + * Fields with default value expressions ("mFoo = ...") are optional, and are automatically + * initialized to the provided default expression, unless explicitly set. + * + * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter} + * while mandatory fields are passed via {@link Builder#Builder constructor}. + */ + @DataClass.Generated.Member + public SampleDataClass setName2(@NonNull String value) { + mName2 = value; + AnnotationValidations.validate( + NonNull.class, null, mName2); + return this; + } + + /** + * Alternatively, when default value computation is expensive, + * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value. + */ + @DataClass.Generated.Member + public SampleDataClass setName4(@NonNull String value) { + mName4 = value; + AnnotationValidations.validate( + NonNull.class, null, mName4); + return this; + } + + /** + * For parcelling, any field type supported by {@link Parcel} is supported out of the box. + * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. + */ + @DataClass.Generated.Member + public SampleDataClass setOtherParcelable(@Nullable AccessibilityNodeInfo value) { + mOtherParcelable = value; + return this; + } + + /** + * Additionally, support for parcelling other types can be added by implementing a + * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. + * + * @see MyDateParcelling an example {@link Parcelling} implementation + */ + @DataClass.Generated.Member + public SampleDataClass setDate(@NonNull Date value) { + mDate = value; + AnnotationValidations.validate( + NonNull.class, null, mDate); + return this; + } + + /** + * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} + * to encourage its reuse. + */ + @DataClass.Generated.Member + public SampleDataClass setPattern(@NonNull Pattern value) { + mPattern = value; + AnnotationValidations.validate( + NonNull.class, null, mPattern); + return this; + } + + /** + * For lists, when using a {@link Builder}, other than a regular + * {@link Builder#setLinkAddresses2(List) setter}, and additional + * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. + */ + @DataClass.Generated.Member + public SampleDataClass setLinkAddresses2(@NonNull List value) { + mLinkAddresses2 = value; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses2); + return this; + } + + /** + * For aesthetics, you may want to consider providing a singular version of the plural field + * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. + * + * @see Builder#addLinkAddress(LinkAddress) + */ + @DataClass.Generated.Member + public SampleDataClass setLinkAddresses(@NonNull ArrayList value) { + mLinkAddresses = value; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses); + return this; + } + + /** + * For array fields, when using a {@link Builder}, vararg argument format is used for + * convenience. + * + * @see Builder#setLinkAddresses4(LinkAddress...) + */ + @DataClass.Generated.Member + public SampleDataClass setLinkAddresses4(@Nullable LinkAddress... value) { + mLinkAddresses4 = value; + return this; + } + + /** + * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to + * getter/constructor/setter/builder parameters, making for a nicer api. + * + * @see #getStateName + * @see Builder#setStateName + */ + @DataClass.Generated.Member + public SampleDataClass setStateName(@StateName @NonNull String value) { + mStateName = value; + + if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED)) + && !(Objects.equals(mStateName, STATE_NAME_ON)) + && !(Objects.equals(mStateName, STATE_NAME_OFF))) { + throw new java.lang.IllegalArgumentException( + "stateName was " + mStateName + " but must be one of: " + + "STATE_NAME_UNDEFINED(" + STATE_NAME_UNDEFINED + "), " + + "STATE_NAME_ON(" + STATE_NAME_ON + "), " + + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")"); + } + + AnnotationValidations.validate( + NonNull.class, null, mStateName); + return this; + } + + /** + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + */ + @DataClass.Generated.Member + public SampleDataClass setFlags(@RequestFlags int value) { + mFlags = value; + + Preconditions.checkFlagsArgument( + mFlags, + FLAG_MANUAL_REQUEST + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_AUGMENTED_REQUEST); + return this; + } + + /** + * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s + */ + @DataClass.Generated.Member + public SampleDataClass setState(@State int value) { + mState = value; + + if (!(mState == STATE_UNDEFINED) + && !(mState == STATE_ON) + && !(mState == STATE_OFF)) { + throw new java.lang.IllegalArgumentException( + "state was " + mState + " but must be one of: " + + "STATE_UNDEFINED(" + STATE_UNDEFINED + "), " + + "STATE_ON(" + STATE_ON + "), " + + "STATE_OFF(" + STATE_OFF + ")"); + } + + return this; + } + + /** + * Fields with certain annotations are automatically validated in constructor + * + * You can see overloads in {@link AnnotationValidations} for a list of currently + * supported ones. + * + * You can also extend support to your custom annotations by creating another corresponding + * overloads like + * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. + * + * @see #SampleDataClass + */ + @DataClass.Generated.Member + public SampleDataClass setStringRes(@StringRes int value) { + mStringRes = value; + AnnotationValidations.validate( + StringRes.class, null, mStringRes); + return this; + } + + /** + * Validation annotations may also have parameters. + * + * Parameter values will be supplied to validation method as name-value pairs. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) + */ + @DataClass.Generated.Member + public SampleDataClass setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) { + mDayOfWeek = value; + AnnotationValidations.validate( + android.annotation.IntRange.class, null, mDayOfWeek, + "from", 0, + "to", 6); + return this; + } + + /** + * Unnamed validation annotation parameter gets supplied to the validating method named as + * "value". + * + * Validation annotations following {@link Each} annotation, will be applied for each + * array/collection element instead. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int) + */ + @DataClass.Generated.Member + public SampleDataClass setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { + mCoords = value; + AnnotationValidations.validate( + Size.class, null, mCoords.length, + "value", 2); + AnnotationValidations.validate( + NonNull.class, null, mCoords); + int coordsSize = mCoords.length; + for (int i = 0; i < coordsSize; i++) { + AnnotationValidations.validate( + FloatRange.class, null, mCoords[i], + "from", 0f); + } + + return this; + } + @Override @DataClass.Generated.Member public String toString() { @@ -829,14 +1102,13 @@ public final class SampleDataClass implements Parcelable { "linkAddresses2 = " + mLinkAddresses2 + ", " + "linkAddresses = " + mLinkAddresses + ", " + "linkAddresses4 = " + java.util.Arrays.toString(mLinkAddresses4) + ", " + - "active = " + mActive + ", " + "stateName = " + mStateName + ", " + "flags = " + requestFlagsToString(mFlags) + ", " + "state = " + stateToString(mState) + ", " + "charSeq = " + charSeq + ", " + "linkAddresses5 = " + java.util.Arrays.toString(mLinkAddresses5) + ", " + "stringRes = " + mStringRes + ", " + - "limited = " + mLimited + ", " + + "dayOfWeek = " + mDayOfWeek + ", " + "coords = " + java.util.Arrays.toString(mCoords) + " }"; } @@ -866,14 +1138,13 @@ public final class SampleDataClass implements Parcelable { && Objects.equals(mLinkAddresses2, that.mLinkAddresses2) && Objects.equals(mLinkAddresses, that.mLinkAddresses) && java.util.Arrays.equals(mLinkAddresses4, that.mLinkAddresses4) - && mActive == that.mActive && Objects.equals(mStateName, that.mStateName) && mFlags == that.mFlags && mState == that.mState && Objects.equals(charSeq, that.charSeq) && java.util.Arrays.equals(mLinkAddresses5, that.mLinkAddresses5) && mStringRes == that.mStringRes - && mLimited == that.mLimited + && mDayOfWeek == that.mDayOfWeek && java.util.Arrays.equals(mCoords, that.mCoords); } @@ -896,14 +1167,13 @@ public final class SampleDataClass implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mLinkAddresses2); _hash = 31 * _hash + Objects.hashCode(mLinkAddresses); _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses4); - _hash = 31 * _hash + Boolean.hashCode(mActive); _hash = 31 * _hash + Objects.hashCode(mStateName); _hash = 31 * _hash + mFlags; _hash = 31 * _hash + mState; _hash = 31 * _hash + Objects.hashCode(charSeq); _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses5); _hash = 31 * _hash + mStringRes; - _hash = 31 * _hash + mLimited; + _hash = 31 * _hash + mDayOfWeek; _hash = 31 * _hash + java.util.Arrays.hashCode(mCoords); return _hash; } @@ -924,14 +1194,13 @@ public final class SampleDataClass implements Parcelable { actionObject.acceptObject(this, "linkAddresses2", mLinkAddresses2); actionObject.acceptObject(this, "linkAddresses", mLinkAddresses); actionObject.acceptObject(this, "linkAddresses4", mLinkAddresses4); - actionObject.acceptObject(this, "active", mActive); actionObject.acceptObject(this, "stateName", mStateName); actionInt.acceptInt(this, "flags", mFlags); actionInt.acceptInt(this, "state", mState); actionObject.acceptObject(this, "charSeq", charSeq); actionObject.acceptObject(this, "linkAddresses5", mLinkAddresses5); actionInt.acceptInt(this, "stringRes", mStringRes); - actionInt.acceptInt(this, "limited", mLimited); + actionInt.acceptInt(this, "dayOfWeek", mDayOfWeek); actionObject.acceptObject(this, "coords", mCoords); } @@ -951,25 +1220,24 @@ public final class SampleDataClass implements Parcelable { action.acceptObject(this, "linkAddresses2", mLinkAddresses2); action.acceptObject(this, "linkAddresses", mLinkAddresses); action.acceptObject(this, "linkAddresses4", mLinkAddresses4); - action.acceptObject(this, "active", mActive); action.acceptObject(this, "stateName", mStateName); action.acceptObject(this, "flags", mFlags); action.acceptObject(this, "state", mState); action.acceptObject(this, "charSeq", charSeq); action.acceptObject(this, "linkAddresses5", mLinkAddresses5); action.acceptObject(this, "stringRes", mStringRes); - action.acceptObject(this, "limited", mLimited); + action.acceptObject(this, "dayOfWeek", mDayOfWeek); action.acceptObject(this, "coords", mCoords); } @DataClass.Generated.Member static Parcelling sParcellingForDate = Parcelling.Cache.get( - DateParcelling.class); + MyDateParcelling.class); static { if (sParcellingForDate == null) { sParcellingForDate = Parcelling.Cache.put( - new DateParcelling()); + new MyDateParcelling()); } } @@ -991,40 +1259,31 @@ public final class SampleDataClass implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } long flg = 0; - if (mActive) flg |= 0x1000; if (mName != null) flg |= 0x8; - if (mName2 != null) flg |= 0x10; if (mOtherParcelable != null) flg |= 0x40; - if (mDate != null) flg |= 0x80; - if (mPattern != null) flg |= 0x100; - if (mLinkAddresses2 != null) flg |= 0x200; - if (mLinkAddresses != null) flg |= 0x400; if (mLinkAddresses4 != null) flg |= 0x800; - if (mStateName != null) flg |= 0x2000; - if (charSeq != null) flg |= 0x10000; - if (mLinkAddresses5 != null) flg |= 0x20000; - if (mCoords != null) flg |= 0x100000; + if (mLinkAddresses5 != null) flg |= 0x10000; dest.writeLong(flg); dest.writeInt(mNum); dest.writeInt(mNum2); dest.writeInt(mNum4); if (mName != null) dest.writeString(mName); - if (mName2 != null) dest.writeString(mName2); + dest.writeString(mName2); dest.writeString(mName4); if (mOtherParcelable != null) dest.writeTypedObject(mOtherParcelable, flags); sParcellingForDate.parcel(mDate, dest, flags); sParcellingForPattern.parcel(mPattern, dest, flags); - if (mLinkAddresses2 != null) dest.writeParcelableList(mLinkAddresses2, flags); - if (mLinkAddresses != null) dest.writeParcelableList(mLinkAddresses, flags); + dest.writeParcelableList(mLinkAddresses2, flags); + dest.writeParcelableList(mLinkAddresses, flags); if (mLinkAddresses4 != null) dest.writeTypedArray(mLinkAddresses4, flags); - if (mStateName != null) dest.writeString(mStateName); + dest.writeString(mStateName); dest.writeInt(mFlags); dest.writeInt(mState); - if (charSeq != null) dest.writeCharSequence(charSeq); + dest.writeCharSequence(charSeq); if (mLinkAddresses5 != null) dest.writeTypedArray(mLinkAddresses5, flags); dest.writeInt(mStringRes); - dest.writeInt(mLimited); - if (mCoords != null) dest.writeFloatArray(mCoords); + dest.writeInt(mDayOfWeek); + dest.writeFloatArray(mCoords); } @Override @@ -1046,35 +1305,28 @@ public final class SampleDataClass implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } long flg = in.readLong(); - boolean active = (flg & 0x1000) != 0; int num = in.readInt(); int num2 = in.readInt(); int num4 = in.readInt(); String name = (flg & 0x8) == 0 ? null : in.readString(); - String name2 = (flg & 0x10) == 0 ? null : in.readString(); + String name2 = in.readString(); String name4 = in.readString(); AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR); Date date = sParcellingForDate.unparcel(in); Pattern pattern = sParcellingForPattern.unparcel(in); - List linkAddresses2 = null; - if ((flg & 0x200) != 0) { - linkAddresses2 = new ArrayList<>(); - in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader()); - } - ArrayList linkAddresses = null; - if ((flg & 0x400) != 0) { - linkAddresses = new ArrayList<>(); - in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader()); - } + List linkAddresses2 = new ArrayList<>(); + in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader()); + ArrayList linkAddresses = new ArrayList<>(); + in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader()); LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); - String stateName = (flg & 0x2000) == 0 ? null : in.readString(); + String stateName = in.readString(); int flags = in.readInt(); int state = in.readInt(); - CharSequence _charSeq = (flg & 0x10000) == 0 ? null : (CharSequence) in.readCharSequence(); - LinkAddress[] linkAddresses5 = (flg & 0x20000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + CharSequence _charSeq = (CharSequence) in.readCharSequence(); + LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); int stringRes = in.readInt(); - int limited = in.readInt(); - float[] coords = (flg & 0x100000) == 0 ? null : in.createFloatArray(); + int dayOfWeek = in.readInt(); + float[] coords = in.createFloatArray(); return new SampleDataClass( num, num2, @@ -1088,14 +1340,13 @@ public final class SampleDataClass implements Parcelable { linkAddresses2, linkAddresses, linkAddresses4, - active, stateName, flags, state, _charSeq, linkAddresses5, stringRes, - limited, + dayOfWeek, coords); } }; @@ -1105,34 +1356,74 @@ public final class SampleDataClass implements Parcelable { */ @SuppressWarnings("WeakerAccess") @DataClass.Generated.Member - public static class Builder - extends android.provider.OneTimeUseBuilder { - - protected int mNum; - protected int mNum2; - protected int mNum4; - protected @Nullable String mName; - protected String mName2; - protected @NonNull String mName4; - protected AccessibilityNodeInfo mOtherParcelable; - protected Date mDate; - protected Pattern mPattern; - protected List mLinkAddresses2; - protected ArrayList mLinkAddresses; - protected @Nullable LinkAddress[] mLinkAddresses4; - protected boolean mActive; - protected @StateName String mStateName; - protected @RequestFlags int mFlags; - protected @State int mState; - protected CharSequence charSeq; - protected LinkAddress[] mLinkAddresses5; - protected @StringRes int mStringRes; - protected @android.annotation.IntRange(from = 0, to = 4) int mLimited; - protected @Size(2) @FloatRange(from = 0f) float[] mCoords; - - protected long mBuilderFieldsSet = 0L; - - public Builder() {}; + public static class Builder { + + private int mNum; + private int mNum2; + private int mNum4; + private @Nullable String mName; + private @NonNull String mName2; + private @NonNull String mName4; + private @Nullable AccessibilityNodeInfo mOtherParcelable; + private @NonNull Date mDate; + private @NonNull Pattern mPattern; + private @NonNull List mLinkAddresses2; + private @NonNull ArrayList mLinkAddresses; + private @Nullable LinkAddress[] mLinkAddresses4; + private @StateName @NonNull String mStateName; + private @RequestFlags int mFlags; + private @State int mState; + private @NonNull CharSequence charSeq; + private @Nullable LinkAddress[] mLinkAddresses5; + private @StringRes int mStringRes; + private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek; + private @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] mCoords; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param num + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + *

+ * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + * @param num2 + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, html links, etc. + * @param num4 + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * @param name + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. + * @param flags + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + * @param linkAddresses5 + * Final fields suppress generating a setter (when setters are requested). + */ + public Builder( + int num, + int num2, + int num4, + @Nullable String name, + @RequestFlags int flags, + @Nullable LinkAddress[] linkAddresses5) { + mNum = num; + mNum2 = num2; + mNum4 = num4; + mName = name; + mFlags = flags; + + Preconditions.checkFlagsArgument( + mFlags, + FLAG_MANUAL_REQUEST + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_AUGMENTED_REQUEST); + mLinkAddresses5 = linkAddresses5; + } /** * Any property javadoc should go onto the field, and will be copied where appropriate, @@ -1143,7 +1434,7 @@ public final class SampleDataClass implements Parcelable { * pieces in multiple places for each field. */ @DataClass.Generated.Member - public Builder setNum(int value) { + public @NonNull Builder setNum(int value) { checkNotUsed(); mBuilderFieldsSet |= 0x1; mNum = value; @@ -1157,7 +1448,7 @@ public final class SampleDataClass implements Parcelable { * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. */ @DataClass.Generated.Member - public Builder setNum2(int value) { + public @NonNull Builder setNum2(int value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; mNum2 = value; @@ -1173,7 +1464,7 @@ public final class SampleDataClass implements Parcelable { * @hide */ @DataClass.Generated.Member - public Builder setNum4(int value) { + public @NonNull Builder setNum4(int value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; mNum4 = value; @@ -1181,11 +1472,10 @@ public final class SampleDataClass implements Parcelable { } /** - * {@link Nullable} fields are considered optional and will not throw an exception if omitted - * (or set to null) when creating an instance either via a {@link Builder} or constructor. + * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. */ @DataClass.Generated.Member - public Builder setName(@Nullable String value) { + public @NonNull Builder setName(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x8; mName = value; @@ -1193,11 +1483,14 @@ public final class SampleDataClass implements Parcelable { } /** - * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * Fields with default value expressions ("mFoo = ...") are optional, and are automatically * initialized to the provided default expression, unless explicitly set. + * + * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter} + * while mandatory fields are passed via {@link Builder#Builder constructor}. */ @DataClass.Generated.Member - public Builder setName2(String value) { + public @NonNull Builder setName2(@NonNull String value) { checkNotUsed(); mBuilderFieldsSet |= 0x10; mName2 = value; @@ -1205,12 +1498,11 @@ public final class SampleDataClass implements Parcelable { } /** - * Fields without {@link Nullable} annotation or default value are considered required. - * - * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + * Alternatively, when default value computation is expensive, + * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value. */ @DataClass.Generated.Member - public Builder setName4(@NonNull String value) { + public @NonNull Builder setName4(@NonNull String value) { checkNotUsed(); mBuilderFieldsSet |= 0x20; mName4 = value; @@ -1222,7 +1514,7 @@ public final class SampleDataClass implements Parcelable { * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. */ @DataClass.Generated.Member - public Builder setOtherParcelable(AccessibilityNodeInfo value) { + public @NonNull Builder setOtherParcelable(@Nullable AccessibilityNodeInfo value) { checkNotUsed(); mBuilderFieldsSet |= 0x40; mOtherParcelable = value; @@ -1233,10 +1525,10 @@ public final class SampleDataClass implements Parcelable { * Additionally, support for parcelling other types can be added by implementing a * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. * - * @see DateParcelling an example {@link Parcelling} implementation + * @see MyDateParcelling an example {@link Parcelling} implementation */ @DataClass.Generated.Member - public Builder setDate(Date value) { + public @NonNull Builder setDate(@NonNull Date value) { checkNotUsed(); mBuilderFieldsSet |= 0x80; mDate = value; @@ -1248,7 +1540,7 @@ public final class SampleDataClass implements Parcelable { * to encourage its reuse. */ @DataClass.Generated.Member - public Builder setPattern(Pattern value) { + public @NonNull Builder setPattern(@NonNull Pattern value) { checkNotUsed(); mBuilderFieldsSet |= 0x100; mPattern = value; @@ -1261,7 +1553,7 @@ public final class SampleDataClass implements Parcelable { * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. */ @DataClass.Generated.Member - public Builder setLinkAddresses2(List value) { + public @NonNull Builder setLinkAddresses2(@NonNull List value) { checkNotUsed(); mBuilderFieldsSet |= 0x200; mLinkAddresses2 = value; @@ -1270,7 +1562,7 @@ public final class SampleDataClass implements Parcelable { /** @see #setLinkAddresses2 */ @DataClass.Generated.Member - public Builder addLinkAddresses2(@NonNull LinkAddress value) { + public @NonNull Builder addLinkAddresses2(LinkAddress value) { // You can refine this method's name by providing item's singular name, e.g.: // @DataClass.PluralOf("item")) mItems = ... @@ -1286,7 +1578,7 @@ public final class SampleDataClass implements Parcelable { * @see Builder#addLinkAddress(LinkAddress) */ @DataClass.Generated.Member - public Builder setLinkAddresses(ArrayList value) { + public @NonNull Builder setLinkAddresses(@NonNull ArrayList value) { checkNotUsed(); mBuilderFieldsSet |= 0x400; mLinkAddresses = value; @@ -1295,7 +1587,7 @@ public final class SampleDataClass implements Parcelable { /** @see #setLinkAddresses */ @DataClass.Generated.Member - public Builder addLinkAddress(@NonNull LinkAddress value) { + public @NonNull Builder addLinkAddress(LinkAddress value) { if (mLinkAddresses == null) setLinkAddresses(new ArrayList<>()); mLinkAddresses.add(value); return this; @@ -1308,37 +1600,13 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setLinkAddresses4(LinkAddress...) */ @DataClass.Generated.Member - public Builder setLinkAddresses4(@Nullable LinkAddress... value) { + public @NonNull Builder setLinkAddresses4(@Nullable LinkAddress... value) { checkNotUsed(); mBuilderFieldsSet |= 0x800; mLinkAddresses4 = value; return this; } - /** - * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods - * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. - */ - @DataClass.Generated.Member - public Builder setActive(boolean value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x1000; - mActive = value; - return this; - } - - /** @see #setActive */ - @DataClass.Generated.Member - public Builder markActive() { - return setActive(true); - } - - /** @see #setActive */ - @DataClass.Generated.Member - public Builder markNotActive() { - return setActive(false); - } - /** * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to * getter/constructor/setter/builder parameters, making for a nicer api. @@ -1347,9 +1615,9 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setStateName */ @DataClass.Generated.Member - public Builder setStateName(@StateName String value) { + public @NonNull Builder setStateName(@StateName @NonNull String value) { checkNotUsed(); - mBuilderFieldsSet |= 0x2000; + mBuilderFieldsSet |= 0x1000; mStateName = value; return this; } @@ -1358,9 +1626,9 @@ public final class SampleDataClass implements Parcelable { * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. */ @DataClass.Generated.Member - public Builder setFlags(@RequestFlags int value) { + public @NonNull Builder setFlags(@RequestFlags int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x4000; + mBuilderFieldsSet |= 0x2000; mFlags = value; return this; } @@ -1369,9 +1637,9 @@ public final class SampleDataClass implements Parcelable { * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s */ @DataClass.Generated.Member - public Builder setState(@State int value) { + public @NonNull Builder setState(@State int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x8000; + mBuilderFieldsSet |= 0x4000; mState = value; return this; } @@ -1380,9 +1648,9 @@ public final class SampleDataClass implements Parcelable { * Making a field public will suppress getter generation in favor of accessing it directly. */ @DataClass.Generated.Member - public Builder setCharSeq(CharSequence value) { + public @NonNull Builder setCharSeq(@NonNull CharSequence value) { checkNotUsed(); - mBuilderFieldsSet |= 0x10000; + mBuilderFieldsSet |= 0x8000; charSeq = value; return this; } @@ -1391,9 +1659,9 @@ public final class SampleDataClass implements Parcelable { * Final fields suppress generating a setter (when setters are requested). */ @DataClass.Generated.Member - public Builder setLinkAddresses5(LinkAddress... value) { + public @NonNull Builder setLinkAddresses5(@Nullable LinkAddress... value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20000; + mBuilderFieldsSet |= 0x10000; mLinkAddresses5 = value; return this; } @@ -1411,9 +1679,9 @@ public final class SampleDataClass implements Parcelable { * @see #SampleDataClass */ @DataClass.Generated.Member - public Builder setStringRes(@StringRes int value) { + public @NonNull Builder setStringRes(@StringRes int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x40000; + mBuilderFieldsSet |= 0x20000; mStringRes = value; return this; } @@ -1426,10 +1694,10 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) */ @DataClass.Generated.Member - public Builder setLimited(@android.annotation.IntRange(from = 0, to = 4) int value) { + public @NonNull Builder setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x80000; - mLimited = value; + mBuilderFieldsSet |= 0x40000; + mDayOfWeek = value; return this; } @@ -1443,30 +1711,23 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public Builder setCoords(@Size(2) @FloatRange(from = 0f) float... value) { + public @NonNull Builder setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { checkNotUsed(); - mBuilderFieldsSet |= 0x100000; + mBuilderFieldsSet |= 0x80000; mCoords = value; return this; } /** Builds the instance. This builder should not be touched after calling this! */ public SampleDataClass build() { - markUsed(); - if ((mBuilderFieldsSet & 0x1) == 0) { - throw new IllegalStateException("Required field not set: num"); - } - if ((mBuilderFieldsSet & 0x2) == 0) { - throw new IllegalStateException("Required field not set: num2"); - } - if ((mBuilderFieldsSet & 0x4) == 0) { - throw new IllegalStateException("Required field not set: num4"); - } + checkNotUsed(); + mBuilderFieldsSet |= 0x100000; // Mark builder used + if ((mBuilderFieldsSet & 0x10) == 0) { mName2 = "Bob"; } if ((mBuilderFieldsSet & 0x20) == 0) { - throw new IllegalStateException("Required field not set: name4"); + mName4 = defaultName4(); } if ((mBuilderFieldsSet & 0x40) == 0) { mOtherParcelable = null; @@ -1487,30 +1748,21 @@ public final class SampleDataClass implements Parcelable { mLinkAddresses4 = null; } if ((mBuilderFieldsSet & 0x1000) == 0) { - mActive = true; - } - if ((mBuilderFieldsSet & 0x2000) == 0) { mStateName = STATE_NAME_UNDEFINED; } if ((mBuilderFieldsSet & 0x4000) == 0) { - throw new IllegalStateException("Required field not set: flags"); - } - if ((mBuilderFieldsSet & 0x8000) == 0) { mState = STATE_UNDEFINED; } - if ((mBuilderFieldsSet & 0x10000) == 0) { + if ((mBuilderFieldsSet & 0x8000) == 0) { charSeq = ""; } if ((mBuilderFieldsSet & 0x20000) == 0) { - throw new IllegalStateException("Required field not set: linkAddresses5"); + mStringRes = 0; } if ((mBuilderFieldsSet & 0x40000) == 0) { - mStringRes = 0; + mDayOfWeek = 3; } if ((mBuilderFieldsSet & 0x80000) == 0) { - mLimited = 3; - } - if ((mBuilderFieldsSet & 0x100000) == 0) { mCoords = new float[] { 0f, 0f }; } SampleDataClass o = new SampleDataClass( @@ -1526,17 +1778,31 @@ public final class SampleDataClass implements Parcelable { mLinkAddresses2, mLinkAddresses, mLinkAddresses4, - mActive, mStateName, mFlags, mState, charSeq, mLinkAddresses5, mStringRes, - mLimited, + mDayOfWeek, mCoords); return o; } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x100000) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } } + @DataClass.Generated( + time = 1565048798524L, + codegenVersion = "1.0.0", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", + inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") + @Deprecated + private void __metadata() {} + } diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java index 71e85ab00eab..663620743af9 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java @@ -49,7 +49,7 @@ public class SampleDataClassTest { private SampleDataClass mSpecimen = newBuilder().build(); private static SampleDataClass.Builder newBuilder() { - return newIncompleteBuilder() + return newInvalidBuilder() .setNum(42) .setNum2(42) .setNum4(42) @@ -57,9 +57,8 @@ public class SampleDataClassTest { .setLinkAddresses5(); } - private static SampleDataClass.Builder newIncompleteBuilder() { - return new SampleDataClass.Builder() - .markActive() + private static SampleDataClass.Builder newInvalidBuilder() { + return new SampleDataClass.Builder(1, 2, 3, "a", 0, null) .setName("some parcelable") .setFlags(SampleDataClass.FLAG_MANUAL_REQUEST); } @@ -91,7 +90,7 @@ public class SampleDataClassTest { public void testCustomParcelling_instanceIsCached() { parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); - assertEquals(1, DateParcelling.sInstanceCount.get()); + assertEquals(1, MyDateParcelling.sInstanceCount.get()); } @Test @@ -149,8 +148,8 @@ public class SampleDataClassTest { } @Test(expected = IllegalStateException.class) - public void testBuilder_throwsWhenRequiredFieldMissing() { - newIncompleteBuilder().build(); + public void testBuilder_performsValidation() { + newInvalidBuilder().build(); } @Test @@ -205,6 +204,11 @@ public class SampleDataClassTest { assertSame(tmpStorage, tmpStorageAgain); } + @Test(expected = IllegalStateException.class) + public void testCustomAnnotationValidation_isRun() { + newBuilder().setDayOfWeek(42).build(); + } + private static T parcelAndUnparcel( T original, Parcelable.Creator creator) { Parcel p = Parcel.obtain(); diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java new file mode 100644 index 000000000000..d88035c31cae --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.annotation.NonNull; +import android.os.SystemClock; + +import com.android.internal.util.DataClass; + +import java.util.concurrent.TimeUnit; + +@DataClass(genBuilder = true) +public class SampleWithCustomBuilder { + + long delayAmount = 0; + @NonNull + TimeUnit delayUnit = TimeUnit.MILLISECONDS; + + long creationTimestamp = SystemClock.uptimeMillis(); + + /** + * You can declare a class named {@code BaseBuilder} to have the generated builder extend from + * it instead. + * + * Same rules apply where defining a non-abstract method will suppress the generation of a + * method with the same signature. + * + * For abstract generatable methods, implementations are generated as normal, but original + * visibility is used, allowing you to hide methods. + * + * Here for example, we hide {@link #setDelayUnit} and {@link #setDelayAmount} from public API, + * replacing it with {@link #setDelay} instead. + */ + // Suppress setter generation for a field that is not supposed to come from user input. + @DataClass.Suppress("setCreationTimestamp") + static abstract class BaseBuilder { + + /** + * Hide methods by declaring them with reduced (package-private) visibility. + */ + abstract Builder setDelayAmount(long value); + + /** + * Alternatively, hide methods by using @hide, to hide them from public API only. + * + * @hide + */ + public abstract Builder setDelayUnit(TimeUnit value); + + /** + * Can provide additional method on the builder, e.g. as a replacement for the ones we've + * just hidden. + */ + public Builder setDelay(long amount, TimeUnit unit) { + setDelayAmount(amount); + setDelayUnit(unit); + return (Builder) this; + } + } + + + + // Code below generated by codegen v1.0.0. + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java + // + // CHECKSTYLE:OFF Generated code + + @DataClass.Generated.Member + /* package-private */ SampleWithCustomBuilder( + long delayAmount, + @NonNull TimeUnit delayUnit, + long creationTimestamp) { + this.delayAmount = delayAmount; + this.delayUnit = delayUnit; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, delayUnit); + this.creationTimestamp = creationTimestamp; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public long getDelayAmount() { + return delayAmount; + } + + @DataClass.Generated.Member + public @NonNull TimeUnit getDelayUnit() { + return delayUnit; + } + + @DataClass.Generated.Member + public long getCreationTimestamp() { + return creationTimestamp; + } + + /** + * A builder for {@link SampleWithCustomBuilder} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder extends BaseBuilder { + + private long delayAmount; + private @NonNull TimeUnit delayUnit; + private long creationTimestamp; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + @DataClass.Generated.Member + @Override + @NonNull Builder setDelayAmount(long value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + delayAmount = value; + return this; + } + + @DataClass.Generated.Member + @Override + public @NonNull Builder setDelayUnit(@NonNull TimeUnit value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + delayUnit = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public SampleWithCustomBuilder build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + delayAmount = 0; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + delayUnit = TimeUnit.MILLISECONDS; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + creationTimestamp = SystemClock.uptimeMillis(); + } + SampleWithCustomBuilder o = new SampleWithCustomBuilder( + delayAmount, + delayUnit, + creationTimestamp); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x8) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1565048799396L, + codegenVersion = "1.0.0", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", + inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") + @Deprecated + private void __metadata() {} + +} diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt index 7ee79f651274..578fb2898480 100644 --- a/tools/codegen/src/com/android/codegen/ClassInfo.kt +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -18,13 +18,7 @@ open class ClassInfo(val sourceLines: List) { e) } val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration - - 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 nestedClasses = classAst.members.filterIsInstance() val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration) .implementedTypes.map { it.asString() } @@ -42,8 +36,4 @@ open class ClassInfo(val sourceLines: List) { .filterNot { it.isTransient || it.isStatic } .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } .apply { lastOrNull()?.isLast = true } - val lazyTransientFields = classAst.fields - .filter { it.isTransient && !it.isStatic } - .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } - .filter { hasMethod("lazyInit${it.NameUpperCamel}") } } \ No newline at end of file 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 { + return clazz + ?.annotations + ?.find { it.nameAsString == DataClassSuppress } + ?.as_() + ?.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(", ")}>" diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index f326fd5601fe..74e79489ad7d 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -9,7 +9,6 @@ import com.github.javaparser.ast.expr.StringLiteralExpr import com.github.javaparser.ast.type.ArrayType import com.github.javaparser.ast.type.ClassOrInterfaceType import com.github.javaparser.javadoc.Javadoc -import java.lang.Long data class FieldInfo( val index: Int, @@ -85,7 +84,7 @@ data class FieldInfo( variableAst.initializer.orElse(null)?.let { return it } classInfo.classAst.methods.find { it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty() - }?.run { "$nameAsString()" }?.let { return it } + }?.run { return "$nameAsString()" } if (FieldClass == "List") return "${classPrinter.memberRef("java.util.Collections.emptyList")}()" return null } @@ -95,7 +94,7 @@ data class FieldInfo( // Generic args val isArray = Type.endsWith("[]") val isList = FieldClass == "List" || FieldClass == "ArrayList" - val fieldBit = "0x${Long.toHexString(1L shl index)}" + val fieldBit = bitAtExpr(index) var isLast = false val isFinal = fieldAst.isFinal val fieldTypeGenegicArgs = when (typeAst) { @@ -143,8 +142,10 @@ data class FieldInfo( } val annotationsAndType by lazy { (annotationsNoInternal + Type).joinToString(" ") } val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } } + + val SetterParamType = if (isArray) "$FieldInnerType..." else Type val annotatedTypeForSetterParam by lazy { - (annotationsNoInternal + if (isArray) "$FieldInnerType..." else Type).joinToString(" ") + (annotationsNoInternal + SetterParamType).joinToString(" ") } // Utilities diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index ab64f4efc8d8..c6e0a064f9b4 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -1,6 +1,7 @@ package com.android.codegen import com.github.javaparser.ast.body.FieldDeclaration +import com.github.javaparser.ast.body.MethodDeclaration import com.github.javaparser.ast.body.VariableDeclarator import com.github.javaparser.ast.expr.* import java.io.File @@ -16,7 +17,7 @@ fun ClassPrinter.generateConstDefs() { val isLiteral = initializer is LiteralExpr || (initializer is UnaryExpr && initializer.expression is LiteralExpr) isLiteral && variable.type.asString() in listOf("int", "String") - } + } && it.annotations.none { it.nameAsString == DataClassSuppressConstDefs } }.flatMap { field -> field.variables.map { it to field } } val intConsts = consts.filter { it.first.type.asString() == "int" } val strConsts = consts.filter { it.first.type.asString() == "String" } @@ -131,7 +132,7 @@ fun ClassPrinter.generateAidl(javaFile: File) { fun ClassPrinter.generateWithers() { fields.forEachApply { val metodName = "with$NameUpperCamel" - if (!hasMethod(metodName, Type)) { + if (!isMethodGenerationSuppressed(metodName, Type)) { generateFieldJavadoc(forceHide = FeatureFlag.WITHERS.hidden) """@$NonNull $GENERATED_MEMBER_HEADER @@ -171,7 +172,7 @@ fun ClassPrinter.generateCopyConstructor() { * ``` */ fun ClassPrinter.generateBuildUpon() { - if (hasMethod("buildUpon")) return + if (isMethodGenerationSuppressed("buildUpon")) return +"/**" +" * Provides an instance of {@link $BuilderClass} with state corresponding to this instance." @@ -195,7 +196,15 @@ fun ClassPrinter.generateBuilder() { val constructorVisibility = if (BuilderClass == CANONICAL_BUILDER_CLASS) "public" else "/* package-*/" - val OneTimeUseBuilder = classRef("android.provider.OneTimeUseBuilder") + val providedSubclassAst = nestedClasses.find { + it.extendedTypes.any { it.nameAsString == BASE_BUILDER_CLASS } + } + + val BuilderSupertype = if (customBaseBuilderAst != null) { + customBaseBuilderAst!!.nameAsString + } else { + "Object" + } +"/**" +" * A builder for {@link $ClassName}" @@ -203,104 +212,155 @@ fun ClassPrinter.generateBuilder() { +" */" +"@SuppressWarnings(\"WeakerAccess\")" +GENERATED_MEMBER_HEADER - "public static class $BuilderClass$genericArgs" { - +"extends $OneTimeUseBuilder<$ClassType>" + !"public static class $BuilderClass$genericArgs" + if (BuilderSupertype != "Object") { + appendSameLine(" extends $BuilderSupertype") } " {" { +"" fields.forEachApply { - +"protected $annotationsAndType $name;" + +"private $annotationsAndType $name;" } +"" - +"protected long mBuilderFieldsSet = 0L;" - +"" - +"$constructorVisibility $BuilderClass() {};" + +"private long mBuilderFieldsSet = 0L;" +"" + val requiredFields = fields.filter { !it.hasDefault } + + generateConstructorJavadoc( + fields = requiredFields, + ClassName = BuilderClass, + hidden = false) + "$constructorVisibility $BuilderClass(" { + requiredFields.forEachLastAware { field, isLast -> + +"${field.annotationsAndType} ${field._name}${if_(!isLast, ",")}" + } + }; " {" { + requiredFields.forEachApply { + generateSetFrom(_name) + } + } + generateBuilderSetters(setterVisibility) generateBuilderBuild() + "private void checkNotUsed() {" { + "if ((mBuilderFieldsSet & ${bitAtExpr(fields.size)}) != 0)" { + "throw new IllegalStateException(" { + +"\"This Builder should not be reused. Use a new Builder instance instead\"" + } + +";" + } + } + rmEmptyLine() } } +private fun ClassPrinter.generateBuilderMethod( + defVisibility: String, + name: String, + ParamAnnotations: String? = null, + paramTypes: List, + paramNames: List = listOf("value"), + genJavadoc: ClassPrinter.() -> Unit, + genBody: ClassPrinter.() -> Unit) { + + val providedMethod = customBaseBuilderAst?.members?.find { + it is MethodDeclaration + && it.nameAsString == name + && it.parameters.map { it.typeAsString } == paramTypes.toTypedArray().toList() + } as? MethodDeclaration + + if ((providedMethod == null || providedMethod.isAbstract) + && name !in builderSuppressedMembers) { + val visibility = providedMethod?.visibility?.asString() ?: defVisibility + val ReturnType = providedMethod?.typeAsString ?: CANONICAL_BUILDER_CLASS + val Annotations = providedMethod?.annotations?.joinToString("\n") + + genJavadoc() + +GENERATED_MEMBER_HEADER + if (providedMethod?.isAbstract == true) +"@Override" + if (!Annotations.isNullOrEmpty()) +Annotations + "$visibility @$NonNull $ReturnType $name(${if_(!ParamAnnotations.isNullOrEmpty(), "$ParamAnnotations ")}${ + paramTypes.zip(paramNames).joinToString(", ") { (Type, paramName) -> "$Type $paramName" } + })" { + genBody() + } + } +} + private fun ClassPrinter.generateBuilderSetters(visibility: String) { fields.forEachApply { val maybeCast = if_(BuilderClass != CANONICAL_BUILDER_CLASS, " ($CANONICAL_BUILDER_CLASS)") - generateFieldJavadoc() - +GENERATED_MEMBER_HEADER - "$visibility $CANONICAL_BUILDER_CLASS set$NameUpperCamel($annotatedTypeForSetterParam value)" { + val setterName = "set$NameUpperCamel" + + generateBuilderMethod( + name = setterName, + defVisibility = visibility, + ParamAnnotations = annotationsNoInternal.joinToString(" "), + paramTypes = listOf(SetterParamType), + genJavadoc = { generateFieldJavadoc() }) { +"checkNotUsed();" +"mBuilderFieldsSet |= $fieldBit;" +"$name = value;" +"return$maybeCast this;" } + val javadocSeeSetter = "/** @see #$setterName */" + val adderName = "add$SingularName" - val javadocSeeSetter = "/** @see #set$NameUpperCamel */" val singularNameCustomizationHint = if (SingularNameOrNull == null) { "// You can refine this method's name by providing item's singular name, e.g.:\n" + "// @DataClass.PluralOf(\"item\")) mItems = ...\n\n" } else "" + if (isList && FieldInnerType != null) { + generateBuilderMethod( + name = adderName, + defVisibility = visibility, + paramTypes = listOf(FieldInnerType), + genJavadoc = { +javadocSeeSetter }) { - +javadocSeeSetter - +GENERATED_MEMBER_HEADER - "$visibility $CANONICAL_BUILDER_CLASS add$SingularName(@$NonNull $FieldInnerType value)" { !singularNameCustomizationHint - +"if ($name == null) set$NameUpperCamel(new $ArrayList<>());" + +"if ($name == null) $setterName(new $ArrayList<>());" +"$name.add(value);" +"return$maybeCast this;" } } if (Type.contains("Map<")) { - val (Key, Value) = fieldTypeGenegicArgs - - +javadocSeeSetter - +GENERATED_MEMBER_HEADER - "$visibility $CANONICAL_BUILDER_CLASS add$SingularName($Key key, $Value value)" { + generateBuilderMethod( + name = adderName, + defVisibility = visibility, + paramTypes = fieldTypeGenegicArgs, + paramNames = listOf("key", "value"), + genJavadoc = { +javadocSeeSetter }) { !singularNameCustomizationHint - +"if ($name == null) set$NameUpperCamel(new $LinkedHashMap());" + +"if ($name == null) $setterName(new $LinkedHashMap());" +"$name.put(key, value);" +"return$maybeCast this;" } } - - if (Type == "boolean") { - +javadocSeeSetter - +GENERATED_MEMBER_HEADER - "$visibility $CANONICAL_BUILDER_CLASS mark$NameUpperCamel()" { - +"return set$NameUpperCamel(true);" - } - - +javadocSeeSetter - +GENERATED_MEMBER_HEADER - "$visibility $CANONICAL_BUILDER_CLASS markNot$NameUpperCamel()" { - +"return set$NameUpperCamel(false);" - } - } } } private fun ClassPrinter.generateBuilderBuild() { +"/** Builds the instance. This builder should not be touched after calling this! */" "public $ClassType build()" { - +"markUsed();" + +"checkNotUsed();" + +"mBuilderFieldsSet |= ${bitAtExpr(fields.size)}; // Mark builder used" + +"" fields.forEachApply { - if (!isNullable || hasDefault) { + if (hasDefault) { "if ((mBuilderFieldsSet & $fieldBit) == 0)" { - if (!isNullable && !hasDefault) { - +"throw new IllegalStateException(\"Required field not set: $nameLowerCamel\");" - } else { - +"$name = $defaultExpr;" - } + +"$name = $defaultExpr;" } } } @@ -348,7 +408,7 @@ fun ClassPrinter.generateParcelable() { } val Parcel = classRef("android.os.Parcel") - if (!hasMethod("writeToParcel", Parcel, "int")) { + if (!isMethodGenerationSuppressed("writeToParcel", Parcel, "int")) { +"@Override" +GENERATED_MEMBER_HEADER "public void writeToParcel($Parcel dest, int flags)" { @@ -390,7 +450,7 @@ fun ClassPrinter.generateParcelable() { } } - if (!hasMethod("describeContents")) { + if (!isMethodGenerationSuppressed("describeContents")) { +"@Override" +GENERATED_MEMBER_HEADER +"public int describeContents() { return 0; }" @@ -442,8 +502,6 @@ fun ClassPrinter.generateParcelable() { FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" FieldClass == "List" || FieldClass == "ArrayList" -> "new ${classRef("java.util.ArrayList")}<>()" -// isArray && FieldInnerType in (PRIMITIVE_TYPES + "String") -> -// "new $FieldInnerType[in.readInt()]" else -> "" } val passContainer = containerInitExpr.isNotEmpty() @@ -519,7 +577,7 @@ fun ClassPrinter.generateParcelable() { } fun ClassPrinter.generateEqualsHashcode() { - if (!hasMethod("equals", "Object")) { + if (!isMethodGenerationSuppressed("equals", "Object")) { +"@Override" +GENERATED_MEMBER_HEADER "public boolean equals(Object o)" { @@ -546,7 +604,7 @@ fun ClassPrinter.generateEqualsHashcode() { } } - if (!hasMethod("hashCode")) { + if (!isMethodGenerationSuppressed("hashCode")) { +"@Override" +GENERATED_MEMBER_HEADER "public int hashCode()" { @@ -572,7 +630,7 @@ fun ClassPrinter.generateEqualsHashcode() { //TODO support IntDef flags? fun ClassPrinter.generateToString() { - if (!hasMethod("toString")) { + if (!isMethodGenerationSuppressed("toString")) { +"@Override" +GENERATED_MEMBER_HEADER "public String toString()" { @@ -599,7 +657,7 @@ fun ClassPrinter.generateToString() { fun ClassPrinter.generateSetters() { fields.forEachApply { - if (!hasMethod("set$NameUpperCamel", Type) + if (!isMethodGenerationSuppressed("set$NameUpperCamel", Type) && !fieldAst.isPublic && !isFinal) { @@ -618,7 +676,7 @@ fun ClassPrinter.generateGetters() { val methodPrefix = if (Type == "boolean") "is" else "get" val methodName = methodPrefix + NameUpperCamel - if (!hasMethod(methodName) && !fieldAst.isPublic) { + if (!isMethodGenerationSuppressed(methodName) && !fieldAst.isPublic) { generateFieldJavadoc(forceHide = FeatureFlag.GETTERS.hidden) +GENERATED_MEMBER_HEADER @@ -662,23 +720,8 @@ fun FieldInfo.generateFieldJavadoc(forceHide: Boolean = false) = classPrinter { } fun FieldInfo.generateSetFrom(source: String) = classPrinter { - !"$name = " - if (Type in PRIMITIVE_TYPES || mayBeNull) { - +"$source;" - } else if (defaultExpr != null) { - "$source != null" { - +"? $source" - +": $defaultExpr;" - } - } else { - val checkNotNull = memberRef("com.android.internal.util.Preconditions.checkNotNull") - +"$checkNotNull($source);" - } - if (isNonEmpty) { - "if ($isEmptyExpr)" { - +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");" - } - } + +"$name = $source;" + generateFieldValidation(field = this@generateSetFrom) } fun ClassPrinter.generateConstructor(visibility: String = "public") { @@ -697,15 +740,18 @@ fun ClassPrinter.generateConstructor(visibility: String = "public") { generateSetFrom(nameLowerCamel) } - generateStateValidation() - generateOnConstructedCallback() } } -private fun ClassPrinter.generateConstructorJavadoc() { +private fun ClassPrinter.generateConstructorJavadoc( + fields: List = this.fields, + ClassName: String = this.ClassName, + hidden: Boolean = FeatureFlag.CONSTRUCTOR.hidden) { if (fields.all { it.javadoc == null } && !FeatureFlag.CONSTRUCTOR.hidden) return +"/**" + +" * Creates a new $ClassName." + +" *" fields.filter { it.javadoc != null }.forEachApply { javadocTextNoAnnotationLines?.apply { +" * @param $nameLowerCamel" @@ -718,87 +764,97 @@ private fun ClassPrinter.generateConstructorJavadoc() { +" */" } -private fun ClassPrinter.generateStateValidation() { - val Size = classRef("android.annotation.Size") - val knownNonValidationAnnotations = internalAnnotations + Nullable - - val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") - fun appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) { - "$validate(" { - !"${annotation.nameAsString}.class, null, $valueToValidate" - val params = when (annotation) { - is MarkerAnnotationExpr -> emptyMap() - is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue) - is NormalAnnotationExpr -> - annotation.pairs.map { it.name.asString() to it.value }.toMap() - else -> throw IllegalStateException() - } - params.forEach { name, value -> - !",\n\"$name\", $value" +private fun ClassPrinter.appendLinesWithContinuationIndent(text: String) { + val lines = text.lines() + if (lines.isNotEmpty()) { + !lines[0] + } + if (lines.size >= 2) { + "" { + lines.drop(1).forEach { + +it } } - +";" } +} - fields.forEachApply { - if (intOrStringDef != null) { - if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) { - +"" - +"//noinspection PointlessBitwiseExpression" - "$Preconditions.checkFlagsArgument(" { - "$name, 0" { - intOrStringDef!!.CONST_NAMES.forEach { - +"| $it" +private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run { + if (isNonEmpty) { + "if ($isEmptyExpr)" { + +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");" + } + } + if (intOrStringDef != null) { + if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) { + +"" + "$Preconditions.checkFlagsArgument(" { + +"$name, " + appendLinesWithContinuationIndent(intOrStringDef!!.CONST_NAMES.joinToString("\n| ")) + } + +";" + } else { + +"" + !"if (" + appendLinesWithContinuationIndent(intOrStringDef!!.CONST_NAMES.joinToString("\n&& ") { + "!(${isEqualToExpr(it)})" + }) + rmEmptyLine(); ") {" { + "throw new ${classRef()}(" { + "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" { + + intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast -> + +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}"""" } } } +";" - } else { - +"" - +"//noinspection PointlessBooleanExpression" - "if (true" { - intOrStringDef!!.CONST_NAMES.forEach { CONST_NAME -> - +"&& !(${isEqualToExpr(CONST_NAME)})" - } - }; rmEmptyLine(); ") {" { - "throw new ${classRef()}(" { - "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" { - - intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast -> - +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}"""" - } - } - } - +";" - } } } + } - val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line - val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter { - it.nameAsString != Each && + val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line + val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter { + it.nameAsString != Each && it.range.orElse(null)?.begin?.line?.let { it >= eachLine } ?: false - } + } - fieldAst.annotations.filterNot { - it.nameAsString == intOrStringDef?.AnnotationName - || it.nameAsString in knownNonValidationAnnotations - || it in perElementValidations - }.forEach { annotation -> - appendValidateCall(annotation, - valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) + val Size = classRef("android.annotation.Size") + fieldAst.annotations.filterNot { + it.nameAsString == intOrStringDef?.AnnotationName + || it.nameAsString in knownNonValidationAnnotations + || it in perElementValidations + }.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) + } + + if (perElementValidations.isNotEmpty()) { + +"int ${nameLowerCamel}Size = $sizeExpr;" + "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" { + perElementValidations.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = elemAtIndexExpr("i")) + } } + } +} - if (perElementValidations.isNotEmpty()) { - +"int ${nameLowerCamel}Size = $sizeExpr;" - "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" { - perElementValidations.forEach { annotation -> - appendValidateCall(annotation, - valueToValidate = elemAtIndexExpr("i")) - } - } +fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) { + val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") + "$validate(" { + !"${annotation.nameAsString}.class, null, $valueToValidate" + val params = when (annotation) { + is MarkerAnnotationExpr -> emptyMap() + is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue) + is NormalAnnotationExpr -> + annotation.pairs.map { it.name.asString() to it.value }.toMap() + else -> throw IllegalStateException() + } + params.forEach { name, value -> + !",\n\"$name\", $value" } } + +";" } private fun ClassPrinter.generateOnConstructedCallback(prefix: String = "") { @@ -845,3 +901,15 @@ fun ClassPrinter.generateForEachField() { } } } + +fun ClassPrinter.generateMetadata(file: File) { + "@$DataClassGenerated(" { + +"time = ${System.currentTimeMillis()}L," + +"codegenVersion = \"$CODEGEN_VERSION\"," + +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\"," + +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\"" + } + +"" + +"@Deprecated" + +"private void __metadata() {}\n" +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt index d1dc88f4a773..1e7a2674006b 100644 --- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -1,6 +1,6 @@ package com.android.codegen -import com.github.javaparser.ast.body.TypeDeclaration +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations import com.github.javaparser.ast.type.ClassOrInterfaceType @@ -8,9 +8,17 @@ import com.github.javaparser.ast.type.Type fun ClassPrinter.getInputSignatures(): List { + return generateInputSignaturesForClass(classAst) + + annotationToString(classAst.annotations.find { it.nameAsString == DataClass }) + + generateInputSignaturesForClass(customBaseBuilderAst) +} + +private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterfaceDeclaration?): List { + if (classAst == null) return emptyList() + return classAst.fields.map { fieldAst -> buildString { - append(fieldAst.modifiers.joinToString(" ") {it.asString()}) + append(fieldAst.modifiers.joinToString(" ") { it.asString() }) append(" ") append(annotationsToString(fieldAst)) append(" ") @@ -20,7 +28,7 @@ fun ClassPrinter.getInputSignatures(): List { } } + classAst.methods.map { methodAst -> buildString { - append(methodAst.modifiers.joinToString(" ") {it.asString()}) + append(methodAst.modifiers.joinToString(" ") { it.asString() }) append(" ") append(annotationsToString(methodAst)) append(" ") @@ -28,19 +36,26 @@ fun ClassPrinter.getInputSignatures(): List { append(" ") append(methodAst.nameAsString) append("(") - append(methodAst.parameters.joinToString(",") {getFullClassName(it.type)}) + append(methodAst.parameters.joinToString(",") { getFullClassName(it.type) }) append(")") } - } + } + ("class ${classAst.nameAsString}" + + " extends ${classAst.extendedTypes.map { getFullClassName(it) }.ifEmpty { listOf("java.lang.Object") }.joinToString(", ")}" + + " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]") } private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String { - return annotatedAst.annotations.joinToString(" ") { - annotationToString(it) - } + return annotatedAst + .annotations + .groupBy { it.nameAsString } // dedupe annotations by name (javaparser bug?) + .values + .joinToString(" ") { + annotationToString(it[0]) + } } -private fun ClassPrinter.annotationToString(ann: AnnotationExpr): String { +private fun ClassPrinter.annotationToString(ann: AnnotationExpr?): String { + if (ann == null) return "" return buildString { append("@") append(getFullClassName(ann.nameAsString)) @@ -78,9 +93,9 @@ private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) { private fun ClassPrinter.getFullClassName(type: Type): String { return if (type is ClassOrInterfaceType) { + getFullClassName(buildString { type.scope.ifPresent { append(it).append(".") } - type.isArrayType append(type.nameAsString) }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(", ") {getFullClassName(it)}}?.let { "<$it>" } ?: "") } else getFullClassName(type.asString()) @@ -100,10 +115,16 @@ private fun ClassPrinter.getFullClassName(className: String): String { val thisPackagePrefix = fileAst.packageDeclaration.map { it.nameAsString + "." }.orElse("") val thisClassPrefix = thisPackagePrefix + classAst.nameAsString + "." - classAst.childNodes.filterIsInstance>().find { + if (classAst.nameAsString == className) return thisPackagePrefix + classAst.nameAsString + + nestedClasses.find { it.nameAsString == className }?.let { return thisClassPrefix + it.nameAsString } + if (className == CANONICAL_BUILDER_CLASS || className == BASE_BUILDER_CLASS) { + return thisClassPrefix + className + } + constDefs.find { it.AnnotationName == className }?.let { return thisClassPrefix + className } if (tryOrNull { Class.forName("java.lang.$className") } != null) { diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 8fafa7ce9b1e..f71bfd302d2e 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -9,9 +9,6 @@ const val INDENT_SINGLE = " " val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") -const val CANONICAL_BUILDER_CLASS = "Builder" -const val GENERATED_BUILDER_CLASS = "GeneratedBuilder" - val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters" @@ -66,10 +63,10 @@ Special methods/etc. you can define: Will be called in constructor, after all the fields have been initialized. This is a good place to put any custom validation logic that you may have - static class $CANONICAL_BUILDER_CLASS extends $GENERATED_BUILDER_CLASS - If a class extending $GENERATED_BUILDER_CLASS is specified, generated builder's setters will + static class $CANONICAL_BUILDER_CLASS extends $BASE_BUILDER_CLASS + If a class extending $BASE_BUILDER_CLASS is specified, generated builder's setters will return the provided $CANONICAL_BUILDER_CLASS type. - $GENERATED_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead + $BASE_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead This allows you to extend the generated builder, adding or overriding any methods you may want @@ -131,7 +128,6 @@ fun main(args: Array) { // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. - // on ${currentTimestamp()} // // DO NOT MODIFY! // @@ -143,14 +139,6 @@ fun main(args: Array) { if (FeatureFlag.CONST_DEFS()) generateConstDefs() - "@$DataClassGenerated(" { - +"time = ${System.currentTimeMillis()}L," - +"codegenVersion = \"$CODEGEN_VERSION\"," - +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\"," - +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\"" - } - +"\n" - if (FeatureFlag.CONSTRUCTOR()) { generateConstructor("public") @@ -160,6 +148,7 @@ fun main(args: Array) { || FeatureFlag.PARCELABLE()) { generateConstructor("/* package-private */") } + if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() if (FeatureFlag.GETTERS()) generateGetters() if (FeatureFlag.SETTERS()) generateSetters() @@ -168,7 +157,6 @@ fun main(args: Array) { if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField() - if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() if (FeatureFlag.WITHERS()) generateWithers() if (FeatureFlag.PARCELABLE()) generateParcelable() @@ -178,6 +166,8 @@ fun main(args: Array) { if (FeatureFlag.AIDL()) generateAidl(file) + generateMetadata(file) + rmEmptyLine() } stringBuilder.append("\n}\n") diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 175eea6ef0d0..7d50ad10de00 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,4 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.0" \ No newline at end of file +const val CODEGEN_VERSION = "1.0.0" + +const val CANONICAL_BUILDER_CLASS = "Builder" +const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index 95c99092e2ab..73ceac41682e 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -1,8 +1,10 @@ package com.android.codegen +import com.github.javaparser.ast.Modifier import com.github.javaparser.ast.expr.AnnotationExpr import com.github.javaparser.ast.expr.Expression import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import com.github.javaparser.ast.nodeTypes.NodeWithModifiers import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter @@ -22,6 +24,8 @@ fun Char.isWhitespaceNonNewline() = isWhitespace() && !isNewline() fun if_(cond: Boolean, then: String) = if (cond) then else "" +fun Any?.as_(): T = this as T + inline infix fun Int.times(action: () -> Unit) { for (i in 1..this) action() } @@ -73,4 +77,15 @@ inline operator fun Array.minus(item: T) = toList().minus(item).t fun currentTimestamp() = DateTimeFormatter .ofLocalizedDateTime(/* date */ FormatStyle.MEDIUM, /* time */ FormatStyle.LONG) .withZone(ZoneId.systemDefault()) - .format(Instant.now()) \ No newline at end of file + .format(Instant.now()) + +val NodeWithModifiers<*>.visibility get() = Modifier.getAccessSpecifier(modifiers) + +fun abort(msg: String): Nothing { + System.err.println("ERROR: $msg") + System.exit(1) + throw InternalError() // can't get here +} + +fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}" + diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index e5ec17a1d18d..26b15aecbe33 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -17,6 +17,8 @@ package android.processor.staledataclass +import com.android.codegen.BASE_BUILDER_CLASS +import com.android.codegen.CANONICAL_BUILDER_CLASS import com.android.codegen.CODEGEN_NAME import com.android.codegen.CODEGEN_VERSION import com.sun.tools.javac.code.Symbol @@ -29,6 +31,7 @@ import javax.annotation.processing.SupportedAnnotationTypes import javax.lang.model.SourceVersion import javax.lang.model.element.AnnotationMirror import javax.lang.model.element.Element +import javax.lang.model.element.ElementKind import javax.lang.model.element.TypeElement import javax.tools.Diagnostic @@ -107,20 +110,30 @@ class StaleDataclassProcessor: AbstractProcessor() { private fun processSingleFile(elementAnnotatedWithGenerated: Element) { - val inputSignatures = elementAnnotatedWithGenerated - .enclosingElement - .enclosedElements - .filterNot { - it.annotationMirrors.any { "Generated" in it.annotationType.toString() } - }.map { - elemToString(it) - }.toSet() + val classElement = elementAnnotatedWithGenerated.enclosingElement + + val inputSignatures = computeSignaturesForClass(classElement) + .plus(computeSignaturesForClass(classElement.enclosedElements.find { + it.kind == ElementKind.CLASS + && !isGenerated(it) + && it.simpleName.toString() == BASE_BUILDER_CLASS + })) + .plus(computeSignaturesForClass(classElement.enclosedElements.find { + it.kind == ElementKind.CLASS + && !isGenerated(it) + && it.simpleName.toString() == CANONICAL_BUILDER_CLASS + })) + .plus(classElement + .annotationMirrors + .find { it.annotationType.toString() == DATACLASS_ANNOTATION_NAME } + .toString()) + .toSet() val annotationParams = elementAnnotatedWithGenerated .annotationMirrors .find { ann -> isGeneratedAnnotation(ann) }!! .elementValues - .map { (k, v) -> k.getSimpleName().toString() to v.getValue() } + .map { (k, v) -> k.simpleName.toString() to v.value } .toMap() val lastGenerated = annotationParams["time"] as Long @@ -140,7 +153,7 @@ class StaleDataclassProcessor: AbstractProcessor() { } val source = repoRoot!!.resolve(sourceRelative) - val clazz = elementAnnotatedWithGenerated.enclosingElement.toString() + val clazz = classElement.toString() if (inputSignatures != lastGenInputSignatures) { error(buildString { @@ -157,6 +170,23 @@ class StaleDataclassProcessor: AbstractProcessor() { } } + private fun computeSignaturesForClass(classElement: Element?): List { + if (classElement == null) return emptyList() + val type = classElement as TypeElement + return classElement + .enclosedElements + .filterNot { + it.kind == ElementKind.CLASS + || it.kind == ElementKind.CONSTRUCTOR + || isGenerated(it) + }.map { + elemToString(it) + } + "class ${classElement.simpleName} extends ${type.superclass} implements [${type.interfaces.joinToString(", ")}]" + } + + private fun isGenerated(it: Element) = + it.annotationMirrors.any { "Generated" in it.annotationType.toString() } + private fun error(msg: String) { processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg) } -- cgit v1.2.3-59-g8ed1b From b3361098edb329d0cf473b3546836daed2c940ca Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 25 Jul 2019 14:05:12 -0700 Subject: Update codegen to match javaparser update Test: . $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/runTest.sh Change-Id: I3e961a9cee5c71bd2195328dcb53dabd61255aa6 --- .../com/android/codegentest/SampleDataClass.java | 2 +- .../codegentest/SampleWithCustomBuilder.java | 2 +- tools/codegen/src/com/android/codegen/ClassInfo.kt | 27 +++++++++++++++++----- .../src/com/android/codegen/ClassPrinter.kt | 2 +- tools/codegen/src/com/android/codegen/FieldInfo.kt | 6 ++--- .../android/codegen/InputSignaturesComputation.kt | 4 ++-- tools/codegen/src/com/android/codegen/Main.kt | 2 ++ tools/codegen/src/com/android/codegen/Utils.kt | 3 +-- 8 files changed, 32 insertions(+), 16 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index b30fde4e9c47..30bb3ef5456b 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -1798,7 +1798,7 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1565048798524L, + time = 1565126122525L, codegenVersion = "1.0.0", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index d88035c31cae..11f03a72c051 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -176,7 +176,7 @@ public class SampleWithCustomBuilder { } @DataClass.Generated( - time = 1565048799396L, + time = 1565126123496L, codegenVersion = "1.0.0", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt index 578fb2898480..5061be2091e5 100644 --- a/tools/codegen/src/com/android/codegen/ClassInfo.kt +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -1,22 +1,37 @@ package com.android.codegen -import com.github.javaparser.JavaParser import com.github.javaparser.ParseProblemException +import com.github.javaparser.ParseResult +import com.github.javaparser.ast.CompilationUnit import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration open class ClassInfo(val sourceLines: List) { private val userSourceCode = (sourceLines + "}").joinToString("\n") - val fileAst = try { - JavaParser.parse(userSourceCode)!! + val fileAst: CompilationUnit = try { + JAVA_PARSER.parse(userSourceCode).throwIfFailed() } catch (e: ParseProblemException) { - throw RuntimeException("Failed to parse code:\n" + + throw parseFailed(cause = e) + } + + fun ParseResult.throwIfFailed(): T { + if (problems.isNotEmpty()) { + throw parseFailed( + desc = this@throwIfFailed.problems.joinToString("\n"), + cause = this@throwIfFailed.problems.mapNotNull { it.cause.orElse(null) }.firstOrNull()) + } + return result.get() + } + + private fun parseFailed(cause: Throwable? = null, desc: String = ""): RuntimeException { + return RuntimeException("Failed to parse code:\n" + userSourceCode .lines() .mapIndexed { lnNum, ln -> "/*$lnNum*/$ln" } - .joinToString("\n"), - e) + .joinToString("\n") + "\n$desc", + cause) } + val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration val nestedClasses = classAst.members.filterIsInstance() diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index f1645ea9a3bb..22b5d8815e7d 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -42,7 +42,7 @@ class ClassPrinter( init { val fieldsWithMissingNullablity = fields.filter { field -> !field.isPrimitive - && Modifier.TRANSIENT !in field.fieldAst.modifiers + && field.fieldAst.modifiers.none { it.keyword == Modifier.Keyword.TRANSIENT } && "@$Nullable" !in field.annotations && "@$NonNull" !in field.annotations } diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index 74e79489ad7d..6b0009ccff76 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -1,6 +1,5 @@ package com.android.codegen -import com.github.javaparser.JavaParser import com.github.javaparser.ast.body.FieldDeclaration import com.github.javaparser.ast.expr.ClassExpr import com.github.javaparser.ast.expr.Name @@ -115,8 +114,9 @@ data class FieldInfo( classPrinter { fieldAst.addAnnotation(SingleMemberAnnotationExpr( Name(ParcelWith), - ClassExpr(JavaParser.parseClassOrInterfaceType( - "$Parcelling.BuiltIn.For$FieldClass")))) + ClassExpr(JAVA_PARSER + .parseClassOrInterfaceType("$Parcelling.BuiltIn.For$FieldClass") + .throwIfFailed()))) } } fieldAst.annotations.map { it.removeComment().toString() } diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt index 1e7a2674006b..1b514d7a74a0 100644 --- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -18,7 +18,7 @@ private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterf return classAst.fields.map { fieldAst -> buildString { - append(fieldAst.modifiers.joinToString(" ") { it.asString() }) + append(fieldAst.modifiers.joinToString(" ") { it.keyword.asString() }) append(" ") append(annotationsToString(fieldAst)) append(" ") @@ -28,7 +28,7 @@ private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterf } } + classAst.methods.map { methodAst -> buildString { - append(methodAst.modifiers.joinToString(" ") { it.asString() }) + append(methodAst.modifiers.joinToString(" ") { it.keyword.asString() }) append(" ") append(annotationsToString(methodAst)) append(" ") diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index f71bfd302d2e..0f932f3c34e1 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -1,5 +1,6 @@ package com.android.codegen +import com.github.javaparser.JavaParser import java.io.File @@ -14,6 +15,7 @@ val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters" const val FLAG_NO_FULL_QUALIFIERS = "--no-full-qualifiers" +val JAVA_PARSER = JavaParser() /** @see [FeatureFlag] */ val USAGE = """ diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index 73ceac41682e..a1f068afa29a 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -79,7 +79,7 @@ fun currentTimestamp() = DateTimeFormatter .withZone(ZoneId.systemDefault()) .format(Instant.now()) -val NodeWithModifiers<*>.visibility get() = Modifier.getAccessSpecifier(modifiers) +val NodeWithModifiers<*>.visibility get() = accessSpecifier fun abort(msg: String): Nothing { System.err.println("ERROR: $msg") @@ -88,4 +88,3 @@ fun abort(msg: String): Nothing { } fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}" - -- cgit v1.2.3-59-g8ed1b From 3b2fe616fd26e9a2022f41589d7062ab12db3768 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 6 Aug 2019 18:47:14 -0700 Subject: Use dataclass codegen on a few initial framework classes Test: presubmit Change-Id: Ide3c21596f6489fdb1db8f72e1436a01b10f56ed --- Android.bp | 5 +- .../java/android/service/autofill/FillContext.java | 185 ++++++++++----- .../java/android/service/autofill/FillRequest.java | 249 ++++++++++++++++----- core/java/com/android/internal/util/DataClass.java | 30 +++ .../src/com/android/codegen/ClassPrinter.kt | 20 +- tools/codegen/src/com/android/codegen/FieldInfo.kt | 1 - .../codegen/src/com/android/codegen/Generators.kt | 8 +- .../android/codegen/InputSignaturesComputation.kt | 2 +- .../staledataclass/StaleDataclassProcessor.kt | 8 +- 9 files changed, 387 insertions(+), 121 deletions(-) (limited to 'tools/codegen') diff --git a/Android.bp b/Android.bp index 7687270fc6f7..51cbba7cfa68 100644 --- a/Android.bp +++ b/Android.bp @@ -798,7 +798,10 @@ java_defaults { "--multi-dex", ], - plugins: ["view-inspector-annotation-processor"], + plugins: [ + "view-inspector-annotation-processor", + "staledataclass-annotation-processor", + ], } filegroup { diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java index 70f434c12863..8331550a7ef5 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -30,6 +30,8 @@ import android.util.ArrayMap; import android.util.SparseIntArray; import android.view.autofill.AutofillId; +import com.android.internal.util.DataClass; + import java.util.LinkedList; /** @@ -46,57 +48,35 @@ import java.util.LinkedList; * to {@link FillResponse.Builder#setClientState(Bundle)} to avoid interpreting * the UI state again while saving. */ +@DataClass( + genHiddenConstructor = true, + genAidl = false) public final class FillContext implements Parcelable { - private final int mRequestId; - private final @NonNull AssistStructure mStructure; - private final @NonNull AutofillId mFocusedId; /** - * Lookup table AutofillId->ViewNode to speed up {@link #findViewNodesByAutofillIds} - * This is purely a cache and can be deleted at any time - */ - @Nullable private ArrayMap mViewNodeLookupTable; - - - /** @hide */ - public FillContext(int requestId, @NonNull AssistStructure structure, - @NonNull AutofillId autofillId) { - mRequestId = requestId; - mStructure = structure; - mFocusedId = autofillId; - } - - private FillContext(Parcel parcel) { - this(parcel.readInt(), parcel.readParcelable(null), parcel.readParcelable(null)); - } - - /** - * Gets the id of the {@link FillRequest fill request} this context + * The id of the {@link FillRequest fill request} this context * corresponds to. This is useful to associate your custom client * state with every request to avoid reinterpreting the UI when saving * user data. - * - * @return The request id. */ - public int getRequestId() { - return mRequestId; - } + private final int mRequestId; /** - * @return The screen content. + * The screen content. */ - @NonNull - public AssistStructure getStructure() { - return mStructure; - } + private final @NonNull AssistStructure mStructure; /** - * @return the AutofillId of the view that triggered autofill. + * The AutofillId of the view that triggered autofill. */ - @NonNull - public AutofillId getFocusedId() { - return mFocusedId; - } + private final @NonNull AutofillId mFocusedId; + + /** + * Lookup table AutofillId->ViewNode to speed up {@link #findViewNodesByAutofillIds} + * This is purely a cache and can be deleted at any time + */ + private transient @Nullable ArrayMap mViewNodeLookupTable; + @Override public String toString() { @@ -105,18 +85,6 @@ public final class FillContext implements Parcelable { return "FillContext [reqId=" + mRequestId + ", focusedId=" + mFocusedId + "]"; } - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeInt(mRequestId); - parcel.writeParcelable(mStructure, flags); - parcel.writeParcelable(mFocusedId, flags); - } - /** * Finds {@link ViewNode ViewNodes} that have the requested ids. * @@ -190,18 +158,119 @@ public final class FillContext implements Parcelable { return foundNodes; } - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - @NonNull - public FillContext createFromParcel(Parcel parcel) { - return new FillContext(parcel); - } + + // Code below generated by codegen v1.0.0. + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillContext.java + // + // CHECKSTYLE:OFF Generated code + + /** + * Creates a new FillContext. + * + * @param requestId + * The id of the {@link FillRequest fill request} this context + * corresponds to. This is useful to associate your custom client + * state with every request to avoid reinterpreting the UI when saving + * user data. + * @param structure + * The screen content. + * @param focusedId + * The AutofillId of the view that triggered autofill. + * @hide + */ + @DataClass.Generated.Member + public FillContext( + int requestId, + @NonNull AssistStructure structure, + @NonNull AutofillId focusedId) { + this.mRequestId = requestId; + this.mStructure = structure; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mStructure); + this.mFocusedId = focusedId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mFocusedId); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The id of the {@link FillRequest fill request} this context + * corresponds to. This is useful to associate your custom client + * state with every request to avoid reinterpreting the UI when saving + * user data. + */ + @DataClass.Generated.Member + public int getRequestId() { + return mRequestId; + } + + /** + * The screen content. + */ + @DataClass.Generated.Member + public @NonNull AssistStructure getStructure() { + return mStructure; + } + + /** + * The AutofillId of the view that triggered autofill. + */ + @DataClass.Generated.Member + public @NonNull AutofillId getFocusedId() { + return mFocusedId; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mRequestId); + dest.writeTypedObject(mStructure, flags); + dest.writeTypedObject(mFocusedId, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { @Override - @NonNull public FillContext[] newArray(int size) { return new FillContext[size]; } + + @Override + @SuppressWarnings({"unchecked", "RedundantCast"}) + public FillContext createFromParcel(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int requestId = in.readInt(); + AssistStructure structure = (AssistStructure) in.readTypedObject(AssistStructure.CREATOR); + AutofillId focusedId = (AutofillId) in.readTypedObject(AutofillId.CREATOR); + return new FillContext( + requestId, + structure, + focusedId); + } }; + + @DataClass.Generated( + time = 1565152135263L, + codegenVersion = "1.0.0", + sourceFile = "frameworks/base/core/java/android/service/autofill/FillContext.java", + inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.app.assist.AssistStructure mStructure\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mFocusedId\nprivate transient @android.annotation.Nullable android.util.ArrayMap mViewNodeLookupTable\npublic @java.lang.Override java.lang.String toString()\npublic @android.annotation.NonNull android.app.assist.AssistStructure.ViewNode[] findViewNodesByAutofillIds(android.view.autofill.AutofillId[])\nclass FillContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false)") + @Deprecated + private void __metadata() {} + } diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 91f77ea0462d..e53ebada55fb 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -24,6 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.view.View; +import com.android.internal.util.DataClass; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -39,6 +40,10 @@ import java.util.List; * * @see AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback) */ +@DataClass( + genToString = true, + genHiddenConstructor = true, + genHiddenConstDefs = true) public final class FillRequest implements Parcelable { /** @@ -63,63 +68,151 @@ public final class FillRequest implements Parcelable { * is called. For example, standard {@link android.widget.TextView} views show an * {@code AUTOFILL} option in the overflow menu that triggers such request. */ - public static final int FLAG_MANUAL_REQUEST = 0x1; + public static final @RequestFlags int FLAG_MANUAL_REQUEST = 0x1; /** * Indicates this request was made using * compatibility mode. */ - public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; - /** @hide */ - @IntDef(flag = true, prefix = { "FLAG_" }, value = { - FLAG_MANUAL_REQUEST, FLAG_COMPATIBILITY_MODE_REQUEST - }) - @Retention(RetentionPolicy.SOURCE) - @interface RequestFlags{} - + /** + * Gets the unique id of this request. + */ private final int mId; - private final @RequestFlags int mFlags; - private final @NonNull ArrayList mContexts; + + /** + * Gets the contexts associated with each previous fill request. + * + *

Note: Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also + * include contexts from requests whose {@link SaveInfo} had the + * {@link SaveInfo#FLAG_DELAY_SAVE} flag. + */ + private final @NonNull List mFillContexts; + + /** + * Gets the latest client state bundle set by the service in a + * {@link FillResponse.Builder#setClientState(Bundle) fill response}. + * + *

Note: Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state + * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} were considered. On + * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of + * an authenticated request through the + * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are + * also considered (and take precedence when set). + * + * @return The client state. + */ private final @Nullable Bundle mClientState; - private FillRequest(@NonNull Parcel parcel) { - mId = parcel.readInt(); - mContexts = new ArrayList<>(); - parcel.readParcelableList(mContexts, null); + /** + * Gets the flags associated with this request. + * + * @return any combination of {@link #FLAG_MANUAL_REQUEST} and + * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. + */ + private final @RequestFlags int mFlags; - mClientState = parcel.readBundle(); - mFlags = parcel.readInt(); + private void onConstructed() { + Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts"); } + + + // Code below generated by codegen v1.0.0. + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillRequest.java + // + // CHECKSTYLE:OFF Generated code + /** @hide */ - public FillRequest(int id, @NonNull ArrayList contexts, - @Nullable Bundle clientState, @RequestFlags int flags) { - mId = id; - mFlags = Preconditions.checkFlagsArgument(flags, - FLAG_MANUAL_REQUEST | FLAG_COMPATIBILITY_MODE_REQUEST); - mContexts = Preconditions.checkCollectionElementsNotNull(contexts, "contexts"); - mClientState = clientState; + @IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_MANUAL_REQUEST, + FLAG_COMPATIBILITY_MODE_REQUEST + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface RequestFlags {} + + /** @hide */ + @DataClass.Generated.Member + public static String requestFlagsToString(@RequestFlags int value) { + return com.android.internal.util.BitUtils.flagsToString( + value, FillRequest::singleRequestFlagsToString); + } + + @DataClass.Generated.Member + static String singleRequestFlagsToString(@RequestFlags int value) { + switch (value) { + case FLAG_MANUAL_REQUEST: + return "FLAG_MANUAL_REQUEST"; + case FLAG_COMPATIBILITY_MODE_REQUEST: + return "FLAG_COMPATIBILITY_MODE_REQUEST"; + default: return Integer.toHexString(value); + } } /** - * Gets the unique id of this request. + * Creates a new FillRequest. + * + * @param id + * Gets the unique id of this request. + * @param fillContexts + * Gets the contexts associated with each previous fill request. + * + *

Note: Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also + * include contexts from requests whose {@link SaveInfo} had the + * {@link SaveInfo#FLAG_DELAY_SAVE} flag. + * @param clientState + * Gets the latest client state bundle set by the service in a + * {@link FillResponse.Builder#setClientState(Bundle) fill response}. + * + *

Note: Prior to Android {@link android.os.Build.VERSION_CODES#P}, only client state + * bundles set by {@link FillResponse.Builder#setClientState(Bundle)} were considered. On + * Android {@link android.os.Build.VERSION_CODES#P} and higher, bundles set in the result of + * an authenticated request through the + * {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE} extra are + * also considered (and take precedence when set). + * @param flags + * Gets the flags associated with this request. + * + * @return any combination of {@link #FLAG_MANUAL_REQUEST} and + * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. + * @hide */ - public int getId() { - return mId; + @DataClass.Generated.Member + public FillRequest( + int id, + @NonNull List fillContexts, + @Nullable Bundle clientState, + @RequestFlags int flags) { + this.mId = id; + this.mFillContexts = fillContexts; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mFillContexts); + this.mClientState = clientState; + this.mFlags = flags; + + Preconditions.checkFlagsArgument( + mFlags, + FLAG_MANUAL_REQUEST + | FLAG_COMPATIBILITY_MODE_REQUEST); + + onConstructed(); } /** - * Gets the flags associated with this request. - * - * @return any combination of {@link #FLAG_MANUAL_REQUEST} and - * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. + * Gets the unique id of this request. */ - public @RequestFlags int getFlags() { - return mFlags; + @DataClass.Generated.Member + public int getId() { + return mId; } /** @@ -129,13 +222,9 @@ public final class FillRequest implements Parcelable { * include contexts from requests whose {@link SaveInfo} had the * {@link SaveInfo#FLAG_DELAY_SAVE} flag. */ + @DataClass.Generated.Member public @NonNull List getFillContexts() { - return mContexts; - } - - @Override - public String toString() { - return "FillRequest: [id=" + mId + ", flags=" + mFlags + ", ctxts= " + mContexts + "]"; + return mFillContexts; } /** @@ -151,33 +240,89 @@ public final class FillRequest implements Parcelable { * * @return The client state. */ + @DataClass.Generated.Member public @Nullable Bundle getClientState() { return mClientState; } + /** + * Gets the flags associated with this request. + * + * @return any combination of {@link #FLAG_MANUAL_REQUEST} and + * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. + */ + @DataClass.Generated.Member + public @RequestFlags int getFlags() { + return mFlags; + } + @Override - public int describeContents() { - return 0; + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "FillRequest { " + + "id = " + mId + ", " + + "fillContexts = " + mFillContexts + ", " + + "clientState = " + mClientState + ", " + + "flags = " + requestFlagsToString(mFlags) + + " }"; } @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeInt(mId); - parcel.writeParcelableList(mContexts, flags); - parcel.writeBundle(mClientState); - parcel.writeInt(mFlags); + @DataClass.Generated.Member + public void writeToParcel(Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mClientState != null) flg |= 0x4; + dest.writeByte(flg); + dest.writeInt(mId); + dest.writeParcelableList(mFillContexts, flags); + if (mClientState != null) dest.writeBundle(mClientState); + dest.writeInt(mFlags); } - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public FillRequest createFromParcel(Parcel parcel) { - return new FillRequest(parcel); - } + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { @Override public FillRequest[] newArray(int size) { return new FillRequest[size]; } + + @Override + @SuppressWarnings({"unchecked", "RedundantCast"}) + public FillRequest createFromParcel(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int id = in.readInt(); + List fillContexts = new ArrayList<>(); + in.readParcelableList(fillContexts, FillContext.class.getClassLoader()); + Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle(); + int flags = in.readInt(); + return new FillRequest( + id, + fillContexts, + clientState, + flags); + } }; + + @DataClass.Generated( + time = 1565152134349L, + codegenVersion = "1.0.0", + sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + @Deprecated + private void __metadata() {} + } diff --git a/core/java/com/android/internal/util/DataClass.java b/core/java/com/android/internal/util/DataClass.java index da33f996b6a7..43539c7c4011 100644 --- a/core/java/com/android/internal/util/DataClass.java +++ b/core/java/com/android/internal/util/DataClass.java @@ -64,11 +64,21 @@ public @interface DataClass { */ boolean genGetters() default true; + /** + * {@link #genGetters} with @hide + */ + boolean genHiddenGetters() default false; + /** * Generates setters for each field. */ boolean genSetters() default false; + /** + * {@link #genSetters} with @hide + */ + boolean genHiddenSetters() default false; + /** * Generates a public constructor with each field initialized from a parameter and optionally * some user-defined state validation at the end. @@ -84,6 +94,11 @@ public @interface DataClass { */ boolean genConstructor() default true; + /** + * {@link #genConstructor} with @hide + */ + boolean genHiddenConstructor() default false; + /** * Generates a Builder for your class. * @@ -92,6 +107,11 @@ public @interface DataClass { */ boolean genBuilder() default false; + /** + * {@link #genBuilder} with @hide + */ + boolean genHiddenBuilder() default false; + /** * Generates a structural {@link Object#equals} + {@link Object#hashCode}. * @@ -125,6 +145,11 @@ public @interface DataClass { */ boolean genCopyConstructor() default false; + /** + * {@link #genCopyConstructor} with @hide + */ + boolean genHiddenCopyConstructor() default false; + /** * Generates constant annotations({@link IntDef}/{@link StringDef}) for any constant groups * with common prefix. @@ -146,6 +171,11 @@ public @interface DataClass { */ boolean genConstDefs() default true; + /** + * {@link #genConstDefs} with @hide + */ + boolean genHiddenConstDefs() default false; + /** * Allows specifying custom parcelling logic based on reusable diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index f1645ea9a3bb..04105599a161 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -122,9 +122,13 @@ class ClassPrinter( if (cliArgs.contains("--$kebabCase")) return true val annotationKey = "gen$upperCamelCase" + val annotationHiddenKey = "genHidden$upperCamelCase" if (dataClassAnnotationFeatures.containsKey(annotationKey)) { return dataClassAnnotationFeatures[annotationKey]!! } + if (dataClassAnnotationFeatures.containsKey(annotationHiddenKey)) { + return dataClassAnnotationFeatures[annotationHiddenKey]!! + } if (cliArgs.contains("--all")) return true if (hidden) return true @@ -144,11 +148,17 @@ class ClassPrinter( } } - val FeatureFlag.hidden - get(): Boolean = when { - cliArgs.contains("--hidden-$kebabCase") -> true - this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden - else -> false + val FeatureFlag.hidden: Boolean + get(): Boolean { + val annotationHiddenKey = "genHidden$upperCamelCase" + if (dataClassAnnotationFeatures.containsKey(annotationHiddenKey)) { + return dataClassAnnotationFeatures[annotationHiddenKey]!! + } + return when { + cliArgs.contains("--hidden-$kebabCase") -> true + this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden + else -> false + } } var currentIndent = INDENT_SINGLE diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index 74e79489ad7d..dc062e1b0283 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -85,7 +85,6 @@ data class FieldInfo( classInfo.classAst.methods.find { it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty() }?.run { return "$nameAsString()" } - if (FieldClass == "List") return "${classPrinter.memberRef("java.util.Collections.emptyList")}()" return null } val hasDefault get() = defaultExpr != null diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index c6e0a064f9b4..914e475cfe41 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -68,12 +68,15 @@ fun ClassPrinter.generateConstDef(consts: List +"$name${if_(!isLast, ",")}" @@ -85,6 +88,9 @@ fun ClassPrinter.generateConstDef(consts: List args.joinToString(", ") {getFullClassName(it)}}?.let { "<$it>" } ?: "") + }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(",") {getFullClassName(it)}}?.let { "<$it>" } ?: "") } else getFullClassName(type.asString()) } diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index 26b15aecbe33..d00def625a39 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -66,10 +66,14 @@ class StaleDataclassProcessor: AbstractProcessor() { if (dataClassAnnotation == null) { dataClassAnnotation = annotations.find { it.qualifiedName.toString() == DATACLASS_ANNOTATION_NAME - } + } ?: return true } - val generatedAnnotatedElements = roundEnv.getElementsAnnotatedWith(generatedAnnotation) + val generatedAnnotatedElements = if (generatedAnnotation != null) { + roundEnv.getElementsAnnotatedWith(generatedAnnotation) + } else { + emptySet() + } generatedAnnotatedElements.forEach { processSingleFile(it) } -- cgit v1.2.3-59-g8ed1b From 71d5e7775e7df89bdbccc1de5ebb6f4742dc760f Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Wed, 11 Sep 2019 13:46:09 -0700 Subject: Fix codegen not accepting relative paths Fixes: 140886378 Test: codegen tests/Codegen/src/com/android/codegentest/SampleDataClass.java; ./frameworks/base/tests/Codegen/runTest.sh Change-Id: Ie2b5c55f35ec7865bf34cf9e99ef7751eb5f8d98 --- tests/Codegen/src/com/android/codegentest/SampleDataClass.java | 6 +++--- .../src/com/android/codegentest/SampleWithCustomBuilder.java | 6 +++--- tools/codegen/src/com/android/codegen/Main.kt | 2 +- tools/codegen/src/com/android/codegen/SharedConstants.kt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 30bb3ef5456b..f0c5baad222b 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.0. + // Code below generated by codegen v1.0.1. // // DO NOT MODIFY! // @@ -1798,8 +1798,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1565126122525L, - codegenVersion = "1.0.0", + time = 1568235365376L, + codegenVersion = "1.0.1", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 11f03a72c051..86f37fe1057e 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -74,7 +74,7 @@ public class SampleWithCustomBuilder { - // Code below generated by codegen v1.0.0. + // Code below generated by codegen v1.0.1. // // DO NOT MODIFY! // @@ -176,8 +176,8 @@ public class SampleWithCustomBuilder { } @DataClass.Generated( - time = 1565126123496L, - codegenVersion = "1.0.0", + time = 1568235366386L, + codegenVersion = "1.0.1", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 0f932f3c34e1..fa2b41adcacb 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -107,7 +107,7 @@ fun main(args: Array) { println(CODEGEN_VERSION) System.exit(0) } - val file = File(args.last()) + val file = File(args.last()).absoluteFile val sourceLinesNoClosingBrace = file.readLines().dropLastWhile { it.startsWith("}") || it.all(Char::isWhitespace) } diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 7d50ad10de00..b2cc81391510 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.0" +const val CODEGEN_VERSION = "1.0.1" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From 6cd8ae53837b898c735cd6e8c8696d291768d34b Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Fri, 27 Sep 2019 10:30:17 -0700 Subject: Move lint disabler comment above CLI hint Long file names may trigger lint error right in the generated warning comment Fixes: 141753810 Test: presubmit Change-Id: I89be669e111e65f80ff8994fd61eaab3c0ae8a6e --- tools/codegen/src/com/android/codegen/Main.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/codegen') diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index fa2b41adcacb..ad5bb9e2e2e8 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -132,11 +132,11 @@ fun main(args: Array) { // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. // // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped - // - // CHECKSTYLE:OFF Generated code + / """ if (FeatureFlag.CONST_DEFS()) generateConstDefs() -- cgit v1.2.3-59-g8ed1b From c49e8eece43b52c638e55fb1c7b3c92735a716e5 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 1 Oct 2019 11:49:15 -0700 Subject: Fix codegen adding random / Fixes: 141934335 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: Ic72d141ed58e332079e7edb7134e02d1447ff643 --- tests/Codegen/src/com/android/codegentest/SampleDataClass.java | 10 +++++----- .../src/com/android/codegentest/SampleWithCustomBuilder.java | 10 +++++----- tools/codegen/src/com/android/codegen/Main.kt | 2 +- tools/codegen/src/com/android/codegen/SharedConstants.kt | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index f0c5baad222b..66c7d06d6ff4 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,14 +342,14 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.1. + // Code below generated by codegen v1.0.3. // // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java - // - // CHECKSTYLE:OFF Generated code + @IntDef(prefix = "STATE_", value = { STATE_UNDEFINED, @@ -1798,8 +1798,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1568235365376L, - codegenVersion = "1.0.1", + time = 1569956013899L, + codegenVersion = "1.0.3", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 86f37fe1057e..c6bc8de3bb9e 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -74,14 +74,14 @@ public class SampleWithCustomBuilder { - // Code below generated by codegen v1.0.1. + // Code below generated by codegen v1.0.3. // // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java - // - // CHECKSTYLE:OFF Generated code + @DataClass.Generated.Member /* package-private */ SampleWithCustomBuilder( @@ -176,8 +176,8 @@ public class SampleWithCustomBuilder { } @DataClass.Generated( - time = 1568235366386L, - codegenVersion = "1.0.1", + time = 1569956014908L, + codegenVersion = "1.0.3", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index ad5bb9e2e2e8..580467433756 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -136,7 +136,7 @@ fun main(args: Array) { // // To regenerate run: // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped - / + """ if (FeatureFlag.CONST_DEFS()) generateConstDefs() diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index b2cc81391510..1e3973eaa022 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.1" +const val CODEGEN_VERSION = "1.0.3" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From 20b6e646e53b464f2208b028a24ac7b654aa7343 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Mon, 30 Sep 2019 15:15:19 -0700 Subject: Fix a few corner-cases with parcelling SparseArrays and Maps Also added a test for some similar parcelling corner cases Test: . frameworks/base/tests/Codegen/runTest.sh Fixes: 141877655 Change-Id: Iad5c3b0a8489206754f6ebe6acb8b56ba9ca174a --- tests/Codegen/runTest.sh | 1 + .../codegentest/ParcelAllTheThingsDataClass.java | 362 +++++++++++++++++++++ .../com/android/codegentest/SampleDataClass.java | 6 +- .../android/codegentest/SampleDataClassTest.java | 34 ++ .../codegentest/SampleWithCustomBuilder.java | 75 ++++- .../codegen/src/com/android/codegen/Generators.kt | 9 +- .../src/com/android/codegen/SharedConstants.kt | 2 +- 7 files changed, 476 insertions(+), 13 deletions(-) create mode 100644 tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java (limited to 'tools/codegen') diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index 614cbb7c9eb6..01522735ec3c 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -13,6 +13,7 @@ else header_and_eval m -j16 codegen_cli && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java && \ cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/CodegenTests.apk && \ diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java new file mode 100644 index 000000000000..2d4125783990 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.android.internal.util.AnnotationValidations; +import com.android.internal.util.DataClass; + +import java.util.List; +import java.util.Map; + +/** + * Additional test for various parcelling corner-cases. + */ +@DataClass( + genBuilder = true, + genAidl = false, + genToString = true) +public class ParcelAllTheThingsDataClass implements Parcelable { + + @NonNull String[] mStringArray = null; + @NonNull int[] mIntArray = null; + @NonNull List mStringList = null; + + @NonNull Map mMap = null; + @NonNull Map mStringMap = null; + + @NonNull SparseArray mSparseArray = null; + @NonNull SparseIntArray mSparseIntArray = null; + + + + // Code below generated by codegen v1.0.4. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java + + + @DataClass.Generated.Member + /* package-private */ ParcelAllTheThingsDataClass( + @NonNull String[] stringArray, + @NonNull int[] intArray, + @NonNull List stringList, + @NonNull Map map, + @NonNull Map stringMap, + @NonNull SparseArray sparseArray, + @NonNull SparseIntArray sparseIntArray) { + this.mStringArray = stringArray; + AnnotationValidations.validate( + NonNull.class, null, mStringArray); + this.mIntArray = intArray; + AnnotationValidations.validate( + NonNull.class, null, mIntArray); + this.mStringList = stringList; + AnnotationValidations.validate( + NonNull.class, null, mStringList); + this.mMap = map; + AnnotationValidations.validate( + NonNull.class, null, mMap); + this.mStringMap = stringMap; + AnnotationValidations.validate( + NonNull.class, null, mStringMap); + this.mSparseArray = sparseArray; + AnnotationValidations.validate( + NonNull.class, null, mSparseArray); + this.mSparseIntArray = sparseIntArray; + AnnotationValidations.validate( + NonNull.class, null, mSparseIntArray); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull String[] getStringArray() { + return mStringArray; + } + + @DataClass.Generated.Member + public @NonNull int[] getIntArray() { + return mIntArray; + } + + @DataClass.Generated.Member + public @NonNull List getStringList() { + return mStringList; + } + + @DataClass.Generated.Member + public @NonNull Map getMap() { + return mMap; + } + + @DataClass.Generated.Member + public @NonNull Map getStringMap() { + return mStringMap; + } + + @DataClass.Generated.Member + public @NonNull SparseArray getSparseArray() { + return mSparseArray; + } + + @DataClass.Generated.Member + public @NonNull SparseIntArray getSparseIntArray() { + return mSparseIntArray; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ParcelAllTheThingsDataClass { " + + "stringArray = " + java.util.Arrays.toString(mStringArray) + ", " + + "intArray = " + java.util.Arrays.toString(mIntArray) + ", " + + "stringList = " + mStringList + ", " + + "map = " + mMap + ", " + + "stringMap = " + mStringMap + ", " + + "sparseArray = " + mSparseArray + ", " + + "sparseIntArray = " + mSparseIntArray + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeStringArray(mStringArray); + dest.writeIntArray(mIntArray); + dest.writeStringList(mStringList); + dest.writeMap(mMap); + dest.writeMap(mStringMap); + dest.writeSparseArray(mSparseArray); + dest.writeSparseIntArray(mSparseIntArray); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public ParcelAllTheThingsDataClass[] newArray(int size) { + return new ParcelAllTheThingsDataClass[size]; + } + + @Override + @SuppressWarnings({"unchecked", "RedundantCast"}) + public ParcelAllTheThingsDataClass createFromParcel(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String[] stringArray = in.createStringArray(); + int[] intArray = in.createIntArray(); + List stringList = new java.util.ArrayList<>(); + in.readStringList(stringList); + Map map = new java.util.LinkedHashMap<>(); + in.readMap(map, SampleWithCustomBuilder.class.getClassLoader()); + Map stringMap = new java.util.LinkedHashMap<>(); + in.readMap(stringMap, String.class.getClassLoader()); + SparseArray sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader()); + SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray(); + return new ParcelAllTheThingsDataClass( + stringArray, + intArray, + stringList, + map, + stringMap, + sparseArray, + sparseIntArray); + } + }; + + /** + * A builder for {@link ParcelAllTheThingsDataClass} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder { + + private @NonNull String[] mStringArray; + private @NonNull int[] mIntArray; + private @NonNull List mStringList; + private @NonNull Map mMap; + private @NonNull Map mStringMap; + private @NonNull SparseArray mSparseArray; + private @NonNull SparseIntArray mSparseIntArray; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + @DataClass.Generated.Member + public @NonNull Builder setStringArray(@NonNull String... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mStringArray = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setIntArray(@NonNull int... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mIntArray = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setStringList(@NonNull List value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mStringList = value; + return this; + } + + /** @see #setStringList */ + @DataClass.Generated.Member + public @NonNull Builder addStringList(String value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mStringList == null) setStringList(new java.util.ArrayList<>()); + mStringList.add(value); + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setMap(@NonNull Map value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mMap = value; + return this; + } + + /** @see #setMap */ + @DataClass.Generated.Member + public @NonNull Builder addMap(String key, SampleWithCustomBuilder value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mMap == null) setMap(new java.util.LinkedHashMap()); + mMap.put(key, value); + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setStringMap(@NonNull Map value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mStringMap = value; + return this; + } + + /** @see #setStringMap */ + @DataClass.Generated.Member + public @NonNull Builder addStringMap(String key, String value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mStringMap == null) setStringMap(new java.util.LinkedHashMap()); + mStringMap.put(key, value); + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setSparseArray(@NonNull SparseArray value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mSparseArray = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setSparseIntArray(@NonNull SparseIntArray value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mSparseIntArray = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public ParcelAllTheThingsDataClass build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mStringArray = null; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mIntArray = null; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mStringList = null; + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mMap = null; + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mStringMap = null; + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mSparseArray = null; + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mSparseIntArray = null; + } + ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass( + mStringArray, + mIntArray, + mStringList, + mMap, + mStringMap, + mSparseArray, + mSparseIntArray); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x80) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1570139502128L, + codegenVersion = "1.0.4", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", + inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") + @Deprecated + private void __metadata() {} + +} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 66c7d06d6ff4..0631f7ded8ae 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.3. + // Code below generated by codegen v1.0.4. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1798,8 +1798,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1569956013899L, - codegenVersion = "1.0.3", + time = 1570139500112L, + codegenVersion = "1.0.4", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java index 663620743af9..c7a773530963 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java @@ -25,9 +25,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; + import android.net.LinkAddress; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.util.SparseArray; +import android.util.SparseIntArray; import androidx.test.runner.AndroidJUnit4; @@ -36,6 +41,9 @@ import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** @@ -209,6 +217,32 @@ public class SampleDataClassTest { newBuilder().setDayOfWeek(42).build(); } + @Test + public void testDataStructures_parcelCorrectly() { + SampleWithCustomBuilder otherParcelable = new SampleWithCustomBuilder.Builder().setDelay(3, SECONDS).build(); + + ParcelAllTheThingsDataClass instance = new ParcelAllTheThingsDataClass.Builder() + .setIntArray(40, 41) + .addMap("foo", otherParcelable) + .setSparseArray(new SparseArray() {{ + put(45, otherParcelable); + }}) + .setSparseIntArray(new SparseIntArray() {{ + put(48, 49); + }}) + .addStringMap("foo2", "fooValue") + .setStringArray("foo", "bar") + .addStringList("foo") + .build(); + + ParcelAllTheThingsDataClass unparceledInstance = + parcelAndUnparcel(instance, ParcelAllTheThingsDataClass.CREATOR); + + // SparseArray and friends don't implement equals + // so just compare string representations instead + assertEquals(instance.toString(), unparceledInstance.toString()); + } + private static T parcelAndUnparcel( T original, Parcelable.Creator creator) { Parcel p = Parcel.obtain(); diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index c6bc8de3bb9e..0f8c663f5d4b 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -17,14 +17,16 @@ package com.android.codegentest; import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; import android.os.SystemClock; import com.android.internal.util.DataClass; import java.util.concurrent.TimeUnit; -@DataClass(genBuilder = true) -public class SampleWithCustomBuilder { +@DataClass(genBuilder = true, genAidl = false, genToString = true) +public class SampleWithCustomBuilder implements Parcelable { long delayAmount = 0; @NonNull @@ -73,8 +75,17 @@ public class SampleWithCustomBuilder { } + private static TimeUnit unparcelDelayUnit(Parcel p) { + return TimeUnit.values()[p.readInt()]; + } + + private void parcelDelayUnit(Parcel p, int flags) { + p.writeInt(delayUnit.ordinal()); + } - // Code below generated by codegen v1.0.3. + + + // Code below generated by codegen v1.0.4. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -112,6 +123,58 @@ public class SampleWithCustomBuilder { return creationTimestamp; } + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "SampleWithCustomBuilder { " + + "delayAmount = " + delayAmount + ", " + + "delayUnit = " + delayUnit + ", " + + "creationTimestamp = " + creationTimestamp + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeLong(delayAmount); + parcelDelayUnit(dest, flags); + dest.writeLong(creationTimestamp); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SampleWithCustomBuilder[] newArray(int size) { + return new SampleWithCustomBuilder[size]; + } + + @Override + @SuppressWarnings({"unchecked", "RedundantCast"}) + public SampleWithCustomBuilder createFromParcel(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long _delayAmount = in.readLong(); + TimeUnit _delayUnit = unparcelDelayUnit(in); + long _creationTimestamp = in.readLong(); + return new SampleWithCustomBuilder( + _delayAmount, + _delayUnit, + _creationTimestamp); + } + }; + /** * A builder for {@link SampleWithCustomBuilder} */ @@ -176,10 +239,10 @@ public class SampleWithCustomBuilder { } @DataClass.Generated( - time = 1569956014908L, - codegenVersion = "1.0.3", + time = 1570139501160L, + codegenVersion = "1.0.4", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", - inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 914e475cfe41..865340566019 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -341,7 +341,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { } } - if (Type.contains("Map<")) { + if (FieldClass.endsWith("Map") && FieldInnerType != null) { generateBuilderMethod( name = adderName, defVisibility = visibility, @@ -533,7 +533,7 @@ fun ClassPrinter.generateParcelable() { } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" && (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") && ParcelMethodsSuffix != "Parcelable") { - !"($Type) " + !"($FieldClass) " } } @@ -541,12 +541,15 @@ fun ClassPrinter.generateParcelable() { when { ParcelMethodsSuffix == "Parcelable" -> methodArgs += "$FieldClass.class.getClassLoader()" + ParcelMethodsSuffix == "SparseArray" -> + methodArgs += "$FieldInnerClass.class.getClassLoader()" ParcelMethodsSuffix == "TypedObject" -> methodArgs += "$FieldClass.CREATOR" ParcelMethodsSuffix == "TypedArray" -> methodArgs += "$FieldInnerClass.CREATOR" + ParcelMethodsSuffix == "Map" -> + methodArgs += "${fieldTypeGenegicArgs[1].substringBefore("<")}.class.getClassLoader()" ParcelMethodsSuffix.startsWith("Parcelable") - || FieldClass == "Map" || (isList || isArray) && FieldInnerType !in PRIMITIVE_TYPES + "String" -> methodArgs += "$FieldInnerClass.class.getClassLoader()" diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 1e3973eaa022..1cc7ef338cc3 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.3" +const val CODEGEN_VERSION = "1.0.4" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From d0a6401ed35d618740f627772b99acf8726a106a Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 3 Oct 2019 15:12:28 -0700 Subject: Add helpful links to `codegen --help` Test: ensure links/email are clickable in terminal Change-Id: I2d9461e98497b8a2af21b0a48ae05c5c110c8484 --- tools/codegen/src/com/android/codegen/Main.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools/codegen') diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 580467433756..fcf1f8260442 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -95,7 +95,13 @@ In addition, for any field mMyField(or myField) of type FieldType you can define you can use with final fields. Version: $CODEGEN_VERSION -Questions? Feedback? Contact: eugenesusla@ + +Questions? Feedback? +Contact: eugenesusla@ +Bug/feature request: http://go/codegen-bug + +Slides: http://go/android-codegen +In-depth example: http://go/SampleDataClass """ fun main(args: Array) { -- cgit v1.2.3-59-g8ed1b From 8cb1a417142e3694e50b004bcd054abd6ecff541 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 3 Oct 2019 17:22:44 -0700 Subject: Add inheritance support for parcelable dataclasses We don't want to recommend this, but some legacy framework classes use inheritance, and it's easy enough to support Fixes: 142081378 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: Ifb7f34abf1dfb871ac01b9a9a38dfee144e5f49a --- tests/Codegen/runTest.sh | 2 + .../codegentest/HierrarchicalDataClassBase.aidl | 19 ++ .../codegentest/HierrarchicalDataClassBase.java | 104 ++++++++++ .../codegentest/HierrarchicalDataClassChild.aidl | 19 ++ .../codegentest/HierrarchicalDataClassChild.java | 126 ++++++++++++ .../codegentest/ParcelAllTheThingsDataClass.java | 72 ++++--- .../com/android/codegentest/SampleDataClass.java | 172 +++++++++++----- .../codegentest/SampleWithCustomBuilder.java | 38 ++-- tools/codegen/src/com/android/codegen/ClassInfo.kt | 5 + .../src/com/android/codegen/ClassPrinter.kt | 10 +- .../codegen/src/com/android/codegen/Generators.kt | 222 +++++++++++---------- tools/codegen/src/com/android/codegen/Main.kt | 3 +- .../src/com/android/codegen/SharedConstants.kt | 2 +- 13 files changed, 600 insertions(+), 194 deletions(-) create mode 100644 tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl create mode 100644 tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java create mode 100644 tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl create mode 100644 tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java (limited to 'tools/codegen') diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index 01522735ec3c..bb3f5b257897 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -14,6 +14,8 @@ else header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \ cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/CodegenTests.apk && \ diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl new file mode 100644 index 000000000000..ab62c83fc1b9 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +parcelable HierrarchicalDataClassBase; diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java new file mode 100644 index 000000000000..9e9ddae4ca76 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * @see HierrarchicalDataClassChild + */ +@DataClass( + genConstructor = false, + genSetters = true) +public class HierrarchicalDataClassBase implements Parcelable { + + private int mBaseData; + + + + // Code below generated by codegen v1.0.5. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java + + + @DataClass.Generated.Member + public int getBaseData() { + return mBaseData; + } + + @DataClass.Generated.Member + public HierrarchicalDataClassBase setBaseData(int value) { + mBaseData = value; + return this; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mBaseData); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected HierrarchicalDataClassBase(android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int baseData = in.readInt(); + + this.mBaseData = baseData; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @android.annotation.NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public HierrarchicalDataClassBase[] newArray(int size) { + return new HierrarchicalDataClassBase[size]; + } + + @Override + public HierrarchicalDataClassBase createFromParcel(android.os.Parcel in) { + return new HierrarchicalDataClassBase(in); + } + }; + + @DataClass.Generated( + time = 1570231100269L, + codegenVersion = "1.0.5", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", + inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") + @Deprecated + private void __metadata() {} + +} diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl new file mode 100644 index 000000000000..a0997222b0af --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +parcelable HierrarchicalDataClassChild; diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java new file mode 100644 index 000000000000..27a6933d25e8 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.annotation.NonNull; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * An example of data classes that extend one another. + * + * Note that some features like constructor generation might not work well due to lack of + * information about the superclass when generating code for subclass. + * + * It is recommended to avoid inheritance in favor of composition for new data classes, + * particularly parcelable ones. + * + * However for legacy classes or where inheritance is desired for allocation efficiency, + * you can either use a technique from this example, opting for mutability/setters, or just write + * constructors by hand. + * + * @see HierrarchicalDataClassBase + */ +@DataClass( + genParcelable = true, + genConstructor = false, + genSetters = true) +public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { + + private @NonNull String mChildData; + + + + // Code below generated by codegen v1.0.5. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java + + + @DataClass.Generated.Member + public @NonNull String getChildData() { + return mChildData; + } + + @DataClass.Generated.Member + public HierrarchicalDataClassChild setChildData(@NonNull String value) { + mChildData = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mChildData); + return this; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + super.writeToParcel(dest, flags); + + dest.writeString(mChildData); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected HierrarchicalDataClassChild(android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + super(in); + + String childData = in.readString(); + + this.mChildData = childData; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mChildData); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public HierrarchicalDataClassChild[] newArray(int size) { + return new HierrarchicalDataClassChild[size]; + } + + @Override + public HierrarchicalDataClassChild createFromParcel(android.os.Parcel in) { + return new HierrarchicalDataClassChild(in); + } + }; + + @DataClass.Generated( + time = 1570231101208L, + codegenVersion = "1.0.5", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") + @Deprecated + private void __metadata() {} + +} diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 2d4125783990..dafece17b7ac 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -48,7 +48,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.4. + // Code below generated by codegen v1.0.5. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -162,6 +162,49 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @DataClass.Generated.Member public int describeContents() { return 0; } + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected ParcelAllTheThingsDataClass(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String[] stringArray = in.createStringArray(); + int[] intArray = in.createIntArray(); + List stringList = new java.util.ArrayList<>(); + in.readStringList(stringList); + Map map = new java.util.LinkedHashMap<>(); + in.readMap(map, SampleWithCustomBuilder.class.getClassLoader()); + Map stringMap = new java.util.LinkedHashMap<>(); + in.readMap(stringMap, String.class.getClassLoader()); + SparseArray sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader()); + SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray(); + + this.mStringArray = stringArray; + AnnotationValidations.validate( + NonNull.class, null, mStringArray); + this.mIntArray = intArray; + AnnotationValidations.validate( + NonNull.class, null, mIntArray); + this.mStringList = stringList; + AnnotationValidations.validate( + NonNull.class, null, mStringList); + this.mMap = map; + AnnotationValidations.validate( + NonNull.class, null, mMap); + this.mStringMap = stringMap; + AnnotationValidations.validate( + NonNull.class, null, mStringMap); + this.mSparseArray = sparseArray; + AnnotationValidations.validate( + NonNull.class, null, mSparseArray); + this.mSparseIntArray = sparseIntArray; + AnnotationValidations.validate( + NonNull.class, null, mSparseIntArray); + + // onConstructed(); // You can define this method to get a callback + } + @DataClass.Generated.Member public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -171,29 +214,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @Override - @SuppressWarnings({"unchecked", "RedundantCast"}) public ParcelAllTheThingsDataClass createFromParcel(Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - String[] stringArray = in.createStringArray(); - int[] intArray = in.createIntArray(); - List stringList = new java.util.ArrayList<>(); - in.readStringList(stringList); - Map map = new java.util.LinkedHashMap<>(); - in.readMap(map, SampleWithCustomBuilder.class.getClassLoader()); - Map stringMap = new java.util.LinkedHashMap<>(); - in.readMap(stringMap, String.class.getClassLoader()); - SparseArray sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader()); - SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray(); - return new ParcelAllTheThingsDataClass( - stringArray, - intArray, - stringList, - map, - stringMap, - sparseArray, - sparseIntArray); + return new ParcelAllTheThingsDataClass(in); } }; @@ -352,8 +374,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1570139502128L, - codegenVersion = "1.0.4", + time = 1570231099316L, + codegenVersion = "1.0.5", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 0631f7ded8ae..1d737361da6f 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.4. + // Code below generated by codegen v1.0.5. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1290,6 +1290,123 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public int describeContents() { return 0; } + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ SampleDataClass(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long flg = in.readLong(); + int num = in.readInt(); + int num2 = in.readInt(); + int num4 = in.readInt(); + String name = (flg & 0x8) == 0 ? null : in.readString(); + String name2 = in.readString(); + String name4 = in.readString(); + AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR); + Date date = sParcellingForDate.unparcel(in); + Pattern pattern = sParcellingForPattern.unparcel(in); + List linkAddresses2 = new ArrayList<>(); + in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader()); + ArrayList linkAddresses = new ArrayList<>(); + in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader()); + LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + String stateName = in.readString(); + int flags = in.readInt(); + int state = in.readInt(); + CharSequence _charSeq = (CharSequence) in.readCharSequence(); + LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + int stringRes = in.readInt(); + int dayOfWeek = in.readInt(); + float[] coords = in.createFloatArray(); + + this.mNum = num; + this.mNum2 = num2; + this.mNum4 = num4; + this.mName = name; + this.mName2 = name2; + AnnotationValidations.validate( + NonNull.class, null, mName2); + this.mName4 = name4; + AnnotationValidations.validate( + NonNull.class, null, mName4); + this.mOtherParcelable = otherParcelable; + this.mDate = date; + AnnotationValidations.validate( + NonNull.class, null, mDate); + this.mPattern = pattern; + AnnotationValidations.validate( + NonNull.class, null, mPattern); + this.mLinkAddresses2 = linkAddresses2; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses2); + this.mLinkAddresses = linkAddresses; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses); + this.mLinkAddresses4 = linkAddresses4; + this.mStateName = stateName; + + if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED)) + && !(Objects.equals(mStateName, STATE_NAME_ON)) + && !(Objects.equals(mStateName, STATE_NAME_OFF))) { + throw new java.lang.IllegalArgumentException( + "stateName was " + mStateName + " but must be one of: " + + "STATE_NAME_UNDEFINED(" + STATE_NAME_UNDEFINED + "), " + + "STATE_NAME_ON(" + STATE_NAME_ON + "), " + + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")"); + } + + AnnotationValidations.validate( + NonNull.class, null, mStateName); + this.mFlags = flags; + + Preconditions.checkFlagsArgument( + mFlags, + FLAG_MANUAL_REQUEST + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_AUGMENTED_REQUEST); + this.mState = state; + + if (!(mState == STATE_UNDEFINED) + && !(mState == STATE_ON) + && !(mState == STATE_OFF)) { + throw new java.lang.IllegalArgumentException( + "state was " + mState + " but must be one of: " + + "STATE_UNDEFINED(" + STATE_UNDEFINED + "), " + + "STATE_ON(" + STATE_ON + "), " + + "STATE_OFF(" + STATE_OFF + ")"); + } + + this.charSeq = _charSeq; + AnnotationValidations.validate( + NonNull.class, null, charSeq); + this.mLinkAddresses5 = linkAddresses5; + this.mStringRes = stringRes; + AnnotationValidations.validate( + StringRes.class, null, mStringRes); + this.mDayOfWeek = dayOfWeek; + AnnotationValidations.validate( + android.annotation.IntRange.class, null, mDayOfWeek, + "from", 0, + "to", 6); + this.mCoords = coords; + AnnotationValidations.validate( + Size.class, null, mCoords.length, + "value", 2); + AnnotationValidations.validate( + NonNull.class, null, mCoords); + int coordsSize = mCoords.length; + for (int i = 0; i < coordsSize; i++) { + AnnotationValidations.validate( + FloatRange.class, null, mCoords[i], + "from", 0f); + } + + + onConstructed(); + } + @DataClass.Generated.Member public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -1299,55 +1416,8 @@ public final class SampleDataClass implements Parcelable { } @Override - @SuppressWarnings({"unchecked", "RedundantCast"}) public SampleDataClass createFromParcel(Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - long flg = in.readLong(); - int num = in.readInt(); - int num2 = in.readInt(); - int num4 = in.readInt(); - String name = (flg & 0x8) == 0 ? null : in.readString(); - String name2 = in.readString(); - String name4 = in.readString(); - AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR); - Date date = sParcellingForDate.unparcel(in); - Pattern pattern = sParcellingForPattern.unparcel(in); - List linkAddresses2 = new ArrayList<>(); - in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader()); - ArrayList linkAddresses = new ArrayList<>(); - in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader()); - LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); - String stateName = in.readString(); - int flags = in.readInt(); - int state = in.readInt(); - CharSequence _charSeq = (CharSequence) in.readCharSequence(); - LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); - int stringRes = in.readInt(); - int dayOfWeek = in.readInt(); - float[] coords = in.createFloatArray(); - return new SampleDataClass( - num, - num2, - num4, - name, - name2, - name4, - otherParcelable, - date, - pattern, - linkAddresses2, - linkAddresses, - linkAddresses4, - stateName, - flags, - state, - _charSeq, - linkAddresses5, - stringRes, - dayOfWeek, - coords); + return new SampleDataClass(in); } }; @@ -1798,8 +1868,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1570139500112L, - codegenVersion = "1.0.4", + time = 1570231097226L, + codegenVersion = "1.0.5", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 0f8c663f5d4b..2efa193e7e2a 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.4. + // Code below generated by codegen v1.0.5. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -151,6 +151,26 @@ public class SampleWithCustomBuilder implements Parcelable { @DataClass.Generated.Member public int describeContents() { return 0; } + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected SampleWithCustomBuilder(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long _delayAmount = in.readLong(); + TimeUnit _delayUnit = unparcelDelayUnit(in); + long _creationTimestamp = in.readLong(); + + this.delayAmount = _delayAmount; + this.delayUnit = _delayUnit; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, delayUnit); + this.creationTimestamp = _creationTimestamp; + + // onConstructed(); // You can define this method to get a callback + } + @DataClass.Generated.Member public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -160,18 +180,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @Override - @SuppressWarnings({"unchecked", "RedundantCast"}) public SampleWithCustomBuilder createFromParcel(Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - long _delayAmount = in.readLong(); - TimeUnit _delayUnit = unparcelDelayUnit(in); - long _creationTimestamp = in.readLong(); - return new SampleWithCustomBuilder( - _delayAmount, - _delayUnit, - _creationTimestamp); + return new SampleWithCustomBuilder(in); } }; @@ -239,8 +249,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1570139501160L, - codegenVersion = "1.0.4", + time = 1570231098303L, + codegenVersion = "1.0.5", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt index 5061be2091e5..92da9dab863b 100644 --- a/tools/codegen/src/com/android/codegen/ClassInfo.kt +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -38,6 +38,11 @@ open class ClassInfo(val sourceLines: List) { val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration) .implementedTypes.map { it.asString() } + val superClass = run { + val superClasses = (fileAst.types[0] as ClassOrInterfaceDeclaration).extendedTypes + if (superClasses.isNonEmpty) superClasses[0] else null + } + val ClassName = classAst.nameAsString private val genericArgsAst = classAst.typeParameters val genericArgs = if (genericArgsAst.isEmpty()) "" else { diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index 1f0d4b8a7ec9..bd72d9e7ec21 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -1,6 +1,7 @@ package com.android.codegen import com.github.javaparser.ast.Modifier +import com.github.javaparser.ast.body.CallableDeclaration import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration import com.github.javaparser.ast.body.TypeDeclaration import com.github.javaparser.ast.expr.* @@ -37,6 +38,7 @@ class ClassPrinter( 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 Parcel by lazy { classRef("android.os.Parcel") } val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") } init { @@ -354,7 +356,9 @@ class ClassPrinter( } fun hasMethod(name: String, vararg argTypes: String): Boolean { - return classAst.methods.any { + val members: List> = + if (name == ClassName) classAst.constructors else classAst.methods + return members.any { it.name.asString() == name && it.parameters.map { it.type.asString() } == argTypes.toList() } @@ -365,6 +369,10 @@ class ClassPrinter( .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } .filter { hasMethod("lazyInit${it.NameUpperCamel}") } + val extendsParcelableClass by lazy { + Parcelable !in superInterfaces && superClass != null + } + init { val builderFactoryOverride = classAst.methods.find { it.isStatic && it.nameAsString == "builder" diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 865340566019..5a95676c1dc8 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -422,6 +422,10 @@ fun ClassPrinter.generateParcelable() { +"// void parcelFieldName(Parcel dest, int flags) { ... }" +"" + if (extendsParcelableClass) { + +"super.writeToParcel(dest, flags);\n" + } + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { +"$flagStorageType flg = 0;" booleanFields.forEachApply { @@ -463,6 +467,123 @@ fun ClassPrinter.generateParcelable() { +"" } + if (!hasMethod(ClassName, Parcel)) { + val visibility = if (classAst.isFinal) "/* package-private */" else "protected" + + +"/** @hide */" + +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})" + +GENERATED_MEMBER_HEADER + "$visibility $ClassName($Parcel in) {" { + +"// You can override field unparcelling by defining methods like:" + +"// static FieldType unparcelFieldName(Parcel in) { ... }" + +"" + + if (extendsParcelableClass) { + +"super(in);\n" + } + + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { + +"$flagStorageType flg = in.read$FlagStorageType();" + } + booleanFields.forEachApply { + +"$Type $_name = (flg & $fieldBit) != 0;" + } + nonBooleanFields.forEachApply { + + // Handle customized parceling + val customParcellingMethod = "unparcel$NameUpperCamel" + if (hasMethod(customParcellingMethod, Parcel)) { + +"$Type $_name = $customParcellingMethod(in);" + } else if (customParcellingClass != null) { + +"$Type $_name = $sParcelling.unparcel(in);" + } else if (hasAnnotation("@$DataClassEnum")) { + val ordinal = "${_name}Ordinal" + +"int $ordinal = in.readInt();" + +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];" + } else { + val methodArgs = mutableListOf() + + // Create container if any + val containerInitExpr = when { + FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" + FieldClass == "List" || FieldClass == "ArrayList" -> + "new ${classRef("java.util.ArrayList")}<>()" + else -> "" + } + val passContainer = containerInitExpr.isNotEmpty() + + // nullcheck + + // "FieldType fieldName = (FieldType)" + if (passContainer) { + methodArgs.add(_name) + !"$Type $_name = " + if (mayBeNull) { + +"null;" + !"if ((flg & $fieldBit) != 0) {" + pushIndent() + +"" + !"$_name = " + } + +"$containerInitExpr;" + } else { + !"$Type $_name = " + if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : " + if (ParcelMethodsSuffix == "StrongInterface") { + !"$FieldClass.Stub.asInterface(" + } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" && + (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") && + ParcelMethodsSuffix != "Parcelable") { + !"($FieldClass) " + } + } + + // Determine method args + when { + ParcelMethodsSuffix == "Parcelable" -> + methodArgs += "$FieldClass.class.getClassLoader()" + ParcelMethodsSuffix == "SparseArray" -> + methodArgs += "$FieldInnerClass.class.getClassLoader()" + ParcelMethodsSuffix == "TypedObject" -> + methodArgs += "$FieldClass.CREATOR" + ParcelMethodsSuffix == "TypedArray" -> + methodArgs += "$FieldInnerClass.CREATOR" + ParcelMethodsSuffix == "Map" -> + methodArgs += "${fieldTypeGenegicArgs[1].substringBefore("<")}.class.getClassLoader()" + ParcelMethodsSuffix.startsWith("Parcelable") + || (isList || isArray) + && FieldInnerType !in PRIMITIVE_TYPES + "String" -> + methodArgs += "$FieldInnerClass.class.getClassLoader()" + } + + // ...in.readFieldType(args...); + when { + ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder" + isArray -> !"in.create$ParcelMethodsSuffix" + else -> !"in.read$ParcelMethodsSuffix" + } + !"(${methodArgs.joinToString(", ")})" + if (ParcelMethodsSuffix == "StrongInterface") !")" + +";" + + // Cleanup if passContainer + if (passContainer && mayBeNull) { + popIndent() + rmEmptyLine() + +"\n}" + } + } + } + + +"" + fields.forEachApply { + !"this." + generateSetFrom(_name) + } + + generateOnConstructedCallback() + } + } + if (classAst.fields.none { it.variables[0].nameAsString == "CREATOR" }) { val Creator = classRef("android.os.Parcelable.Creator") @@ -477,107 +598,8 @@ fun ClassPrinter.generateParcelable() { } +"@Override" - +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})" "public $ClassName createFromParcel($Parcel in)" { - +"// You can override field unparcelling by defining methods like:" - +"// static FieldType unparcelFieldName(Parcel in) { ... }" - +"" - if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { - +"$flagStorageType flg = in.read$FlagStorageType();" - } - booleanFields.forEachApply { - +"$Type $_name = (flg & $fieldBit) != 0;" - } - nonBooleanFields.forEachApply { - - // Handle customized parceling - val customParcellingMethod = "unparcel$NameUpperCamel" - if (hasMethod(customParcellingMethod, Parcel)) { - +"$Type $_name = $customParcellingMethod(in);" - } else if (customParcellingClass != null) { - +"$Type $_name = $sParcelling.unparcel(in);" - } else if (hasAnnotation("@$DataClassEnum")) { - val ordinal = "${_name}Ordinal" - +"int $ordinal = in.readInt();" - +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];" - } else { - val methodArgs = mutableListOf() - - // Create container if any - val containerInitExpr = when { - FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" - FieldClass == "List" || FieldClass == "ArrayList" -> - "new ${classRef("java.util.ArrayList")}<>()" - else -> "" - } - val passContainer = containerInitExpr.isNotEmpty() - - // nullcheck + - // "FieldType fieldName = (FieldType)" - if (passContainer) { - methodArgs.add(_name) - !"$Type $_name = " - if (mayBeNull) { - +"null;" - !"if ((flg & $fieldBit) != 0) {" - pushIndent() - +"" - !"$_name = " - } - +"$containerInitExpr;" - } else { - !"$Type $_name = " - if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : " - if (ParcelMethodsSuffix == "StrongInterface") { - !"$FieldClass.Stub.asInterface(" - } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" && - (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") && - ParcelMethodsSuffix != "Parcelable") { - !"($FieldClass) " - } - } - - // Determine method args - when { - ParcelMethodsSuffix == "Parcelable" -> - methodArgs += "$FieldClass.class.getClassLoader()" - ParcelMethodsSuffix == "SparseArray" -> - methodArgs += "$FieldInnerClass.class.getClassLoader()" - ParcelMethodsSuffix == "TypedObject" -> - methodArgs += "$FieldClass.CREATOR" - ParcelMethodsSuffix == "TypedArray" -> - methodArgs += "$FieldInnerClass.CREATOR" - ParcelMethodsSuffix == "Map" -> - methodArgs += "${fieldTypeGenegicArgs[1].substringBefore("<")}.class.getClassLoader()" - ParcelMethodsSuffix.startsWith("Parcelable") - || (isList || isArray) - && FieldInnerType !in PRIMITIVE_TYPES + "String" -> - methodArgs += "$FieldInnerClass.class.getClassLoader()" - } - - // ...in.readFieldType(args...); - when { - ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder" - isArray -> !"in.create$ParcelMethodsSuffix" - else -> !"in.read$ParcelMethodsSuffix" - } - !"(${methodArgs.joinToString(", ")})" - if (ParcelMethodsSuffix == "StrongInterface") !")" - +";" - - // Cleanup if passContainer - if (passContainer && mayBeNull) { - popIndent() - rmEmptyLine() - +"\n}" - } - } - } - "return new $ClassType(" { - fields.forEachTrimmingTrailingComma { - +"$_name," - } - } + ";" + +"return new $ClassName(in);" } rmEmptyLine() } + ";" diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 580467433756..c91eca94a9c0 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -146,8 +146,7 @@ fun main(args: Array) { generateConstructor("public") } else if (FeatureFlag.BUILDER() || FeatureFlag.COPY_CONSTRUCTOR() - || FeatureFlag.WITHERS() - || FeatureFlag.PARCELABLE()) { + || FeatureFlag.WITHERS()) { generateConstructor("/* package-private */") } if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 1cc7ef338cc3..a36f2c838787 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.4" +const val CODEGEN_VERSION = "1.0.5" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From 05ef53ed1cbda4476ddef9bc1b9bcb11b3501680 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Mon, 7 Oct 2019 12:04:04 -0700 Subject: Fix some stale dataclass detector false positives Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: I562bb4f238bdb28af49804e77c956e41a7bd198d --- tests/Codegen/runTest.sh | 1 + .../codegentest/HierrarchicalDataClassBase.java | 6 +- .../codegentest/HierrarchicalDataClassChild.java | 6 +- .../codegentest/ParcelAllTheThingsDataClass.java | 6 +- .../com/android/codegentest/SampleDataClass.java | 6 +- .../codegentest/SampleWithCustomBuilder.java | 6 +- .../StaleDataclassDetectorFalsePositivesTest.java | 71 ++++++++++++++++++++++ .../src/com/android/codegen/SharedConstants.kt | 2 +- .../staledataclass/StaleDataclassProcessor.kt | 5 ++ 9 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java (limited to 'tools/codegen') diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index 82e9f03e08e9..0e90deaadd61 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -16,6 +16,7 @@ else header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \ cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \ diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 9e9ddae4ca76..10eba6a899ad 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.5. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -94,8 +94,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1570231100269L, - codegenVersion = "1.0.5", + time = 1570576455287L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 27a6933d25e8..1085a6a1636a 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.5. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -116,8 +116,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1570231101208L, - codegenVersion = "1.0.5", + time = 1570576456245L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index dafece17b7ac..75ef963c7995 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -48,7 +48,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.5. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -374,8 +374,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1570231099316L, - codegenVersion = "1.0.5", + time = 1570576454326L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 1d737361da6f..14010a9cfb1d 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.5. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1868,8 +1868,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1570231097226L, - codegenVersion = "1.0.5", + time = 1570576452225L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 2efa193e7e2a..b5f6c73a8aef 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.5. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -249,8 +249,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1570231098303L, - codegenVersion = "1.0.5", + time = 1570576453295L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java new file mode 100644 index 000000000000..0ce8aba9b28f --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.annotation.NonNull; + +import com.android.internal.util.DataClass; + +/** + * Test for some false positive pitfalls for + * {@link android.processor.staledataclass.StaleDataclassProcessor} + * + * Relies on the detector being run, failing the build should any of things here falsely + * register as stale. + */ +@DataClass(genConstructor = false, genBuilder = false) +public class StaleDataclassDetectorFalsePositivesTest { + + /** Interfaces should be ignored */ + public interface SomeListener { + void onEvent(); + } + + /** Enums should be ignored */ + private enum SomeEnum { ONE, TWO } + + /** Annotations should be ignored */ + public @interface SomeAnnotation {} + + /* Static initializers should be ignored */ + static {} + + /* Initializers should be ignored */ + {} + + /** Unrelated methods should be noted, without triggering staleness false positives */ + public @NonNull String someMethod(int param) { return null; } + + + + // Code below generated by codegen v1.0.7. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java + + + @DataClass.Generated( + time = 1570576457249L, + codegenVersion = "1.0.7", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", + inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") + @Deprecated + private void __metadata() {} + +} diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index a36f2c838787..47f774f07f16 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.5" +const val CODEGEN_VERSION = "1.0.7" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index d00def625a39..7fe21c7aab3e 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -182,6 +182,11 @@ class StaleDataclassProcessor: AbstractProcessor() { .filterNot { it.kind == ElementKind.CLASS || it.kind == ElementKind.CONSTRUCTOR + || it.kind == ElementKind.INTERFACE + || it.kind == ElementKind.ENUM + || it.kind == ElementKind.ANNOTATION_TYPE + || it.kind == ElementKind.INSTANCE_INIT + || it.kind == ElementKind.STATIC_INIT || isGenerated(it) }.map { elemToString(it) -- cgit v1.2.3-59-g8ed1b From f821caa26606f02d372f3c3873938b8bf6566c78 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Fri, 4 Oct 2019 14:51:29 -0700 Subject: Few minor codegen fixes - (partially) fix annotations with array arg trigger stale false positive - support boxed primitives parcelling - support excluding generated code from Intellij auto-formatting Fixes: 142086873, 142149372 Bug: 142132310 Test: . frameworks/base/tests/Codegen/runTest.sh + manually test auto-formatting Change-Id: Ib6a44dee59f840cc11066aa9ff3517d957d1d010 --- .../codegentest/HierrarchicalDataClassBase.java | 10 ++-- .../codegentest/HierrarchicalDataClassChild.java | 10 ++-- .../codegentest/ParcelAllTheThingsDataClass.java | 54 ++++++++++++++++++---- .../com/android/codegentest/SampleDataClass.java | 10 ++-- .../codegentest/SampleWithCustomBuilder.java | 10 ++-- .../StaleDataclassDetectorFalsePositivesTest.java | 10 ++-- tools/codegen/src/com/android/codegen/FieldInfo.kt | 2 +- .../codegen/src/com/android/codegen/Generators.kt | 10 +--- .../android/codegen/InputSignaturesComputation.kt | 8 ++++ tools/codegen/src/com/android/codegen/Main.kt | 5 ++ .../src/com/android/codegen/SharedConstants.kt | 2 +- tools/codegen/src/com/android/codegen/Utils.kt | 12 +++-- 12 files changed, 105 insertions(+), 38 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 10eba6a899ad..35150538d3d1 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,13 +32,17 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -94,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1570576455287L, - codegenVersion = "1.0.7", + time = 1570828332402L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 1085a6a1636a..c86740926813 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,13 +46,17 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -116,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1570576456245L, - codegenVersion = "1.0.7", + time = 1570828333399L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 75ef963c7995..8d097a0c978e 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -16,6 +16,7 @@ package com.android.codegentest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -46,15 +47,22 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @NonNull SparseArray mSparseArray = null; @NonNull SparseIntArray mSparseIntArray = null; + @SuppressWarnings({"WeakerAccess"}) + @Nullable Boolean mNullableBoolean = null; - // Code below generated by codegen v1.0.7. + + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -65,7 +73,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @NonNull Map map, @NonNull Map stringMap, @NonNull SparseArray sparseArray, - @NonNull SparseIntArray sparseIntArray) { + @NonNull SparseIntArray sparseIntArray, + @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) { this.mStringArray = stringArray; AnnotationValidations.validate( NonNull.class, null, mStringArray); @@ -87,6 +96,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { this.mSparseIntArray = sparseIntArray; AnnotationValidations.validate( NonNull.class, null, mSparseIntArray); + this.mNullableBoolean = nullableBoolean; // onConstructed(); // You can define this method to get a callback } @@ -126,6 +136,11 @@ public class ParcelAllTheThingsDataClass implements Parcelable { return mSparseIntArray; } + @DataClass.Generated.Member + public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() { + return mNullableBoolean; + } + @Override @DataClass.Generated.Member public String toString() { @@ -139,7 +154,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { "map = " + mMap + ", " + "stringMap = " + mStringMap + ", " + "sparseArray = " + mSparseArray + ", " + - "sparseIntArray = " + mSparseIntArray + + "sparseIntArray = " + mSparseIntArray + ", " + + "nullableBoolean = " + mNullableBoolean + " }"; } @@ -149,6 +165,9 @@ public class ParcelAllTheThingsDataClass implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } + int flg = 0; + if (mNullableBoolean != null) flg |= 0x80; + dest.writeInt(flg); dest.writeStringArray(mStringArray); dest.writeIntArray(mIntArray); dest.writeStringList(mStringList); @@ -156,6 +175,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { dest.writeMap(mStringMap); dest.writeSparseArray(mSparseArray); dest.writeSparseIntArray(mSparseIntArray); + if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean); } @Override @@ -169,6 +189,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } + int flg = in.readInt(); String[] stringArray = in.createStringArray(); int[] intArray = in.createIntArray(); List stringList = new java.util.ArrayList<>(); @@ -179,6 +200,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { in.readMap(stringMap, String.class.getClassLoader()); SparseArray sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader()); SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray(); + Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean(); this.mStringArray = stringArray; AnnotationValidations.validate( @@ -201,6 +223,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { this.mSparseIntArray = sparseIntArray; AnnotationValidations.validate( NonNull.class, null, mSparseIntArray); + this.mNullableBoolean = nullableBoolean; // onConstructed(); // You can define this method to get a callback } @@ -233,6 +256,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { private @NonNull Map mStringMap; private @NonNull SparseArray mSparseArray; private @NonNull SparseIntArray mSparseIntArray; + private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean; private long mBuilderFieldsSet = 0L; @@ -328,10 +352,18 @@ public class ParcelAllTheThingsDataClass implements Parcelable { return this; } + @DataClass.Generated.Member + public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mNullableBoolean = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public ParcelAllTheThingsDataClass build() { checkNotUsed(); - mBuilderFieldsSet |= 0x80; // Mark builder used + mBuilderFieldsSet |= 0x100; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mStringArray = null; @@ -354,6 +386,9 @@ public class ParcelAllTheThingsDataClass implements Parcelable { if ((mBuilderFieldsSet & 0x40) == 0) { mSparseIntArray = null; } + if ((mBuilderFieldsSet & 0x80) == 0) { + mNullableBoolean = null; + } ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass( mStringArray, mIntArray, @@ -361,12 +396,13 @@ public class ParcelAllTheThingsDataClass implements Parcelable { mMap, mStringMap, mSparseArray, - mSparseIntArray); + mSparseIntArray, + mNullableBoolean); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x80) != 0) { + if ((mBuilderFieldsSet & 0x100) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -374,10 +410,10 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1570576454326L, - codegenVersion = "1.0.7", + time = 1570828331396L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", - inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") + inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated private void __metadata() {} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 14010a9cfb1d..d014d6d27ce8 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,13 +342,17 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @IntDef(prefix = "STATE_", value = { @@ -1868,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1570576452225L, - codegenVersion = "1.0.7", + time = 1570828329319L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index b5f6c73a8aef..1c87e8f0ab36 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,13 +85,17 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -249,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1570576453295L, - codegenVersion = "1.0.7", + time = 1570828330331L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 0ce8aba9b28f..27af37fe23ab 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,18 +51,22 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated( - time = 1570576457249L, - codegenVersion = "1.0.7", + time = 1570828334384L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index ba00264f5f5e..1a7fd6e241aa 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -191,7 +191,7 @@ data class FieldInfo( * Parcel.write* and Parcel.read* method name wildcard values */ val ParcelMethodsSuffix = when { - FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + + FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES + listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle", "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") -> FieldClass diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 5a95676c1dc8..0ebb3cf2a447 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -854,6 +854,7 @@ private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run { it.nameAsString == intOrStringDef?.AnnotationName || it.nameAsString in knownNonValidationAnnotations || it in perElementValidations + || it.args.any { (_, value) -> value is ArrayInitializerExpr } }.forEach { annotation -> appendValidateCall(annotation, valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) @@ -874,14 +875,7 @@ fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate: val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") "$validate(" { !"${annotation.nameAsString}.class, null, $valueToValidate" - val params = when (annotation) { - is MarkerAnnotationExpr -> emptyMap() - is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue) - is NormalAnnotationExpr -> - annotation.pairs.map { it.name.asString() to it.value }.toMap() - else -> throw IllegalStateException() - } - params.forEach { name, value -> + annotation.args.forEach { name, value -> !",\n\"$name\", $value" } } diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt index 24cf4690dae1..d6953c00fc0b 100644 --- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -87,6 +87,14 @@ private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) { is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L") is LongLiteralExpr -> sb.append(ex.asLong()).append("L") is DoubleLiteralExpr -> sb.append(ex.asDouble()) + is ArrayInitializerExpr -> { + sb.append("{") + ex.values.forEachLastAware { arrayElem, isLast -> + appendExpr(sb, arrayElem) + if (!isLast) sb.append(", ") + } + sb.append("}") + } else -> sb.append(ex) } } diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 039f7b2fc627..ce83d3dc8e51 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -9,6 +9,7 @@ const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" const val INDENT_SINGLE = " " val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") +val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character" val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") @@ -142,6 +143,10 @@ fun main(args: Array) { // // To regenerate run: // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off """ diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 47f774f07f16..8c4583f68c4b 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.7" +const val CODEGEN_VERSION = "1.0.8" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index a1f068afa29a..e703397214eb 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -1,9 +1,6 @@ package com.android.codegen -import com.github.javaparser.ast.Modifier -import com.github.javaparser.ast.expr.AnnotationExpr -import com.github.javaparser.ast.expr.Expression -import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.nodeTypes.NodeWithModifiers import java.time.Instant import java.time.ZoneId @@ -88,3 +85,10 @@ fun abort(msg: String): Nothing { } fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}" + +val AnnotationExpr.args: Map get() = when (this) { + is MarkerAnnotationExpr -> emptyMap() + is SingleMemberAnnotationExpr -> mapOf("value" to memberValue) + is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap() + else -> throw IllegalArgumentException("Unknown annotation expression: $this") +} -- cgit v1.2.3-59-g8ed1b From 1bfb5e709728959eb77cb756a2db80ecdc7549e1 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Wed, 16 Oct 2019 10:12:47 -0700 Subject: Add @NonNull to some generated methods' args Test: . frameworks/base/tests/Codegen/runTest.sh Fixes: 142783758 Change-Id: I7303d420efd59389c324b3e399fcb3532b5d6c37 --- api/current.txt | 2 +- core/java/android/app/AsyncNotedAppOp.java | 76 +++++++++++++++------- .../codegentest/HierrarchicalDataClassBase.java | 12 ++-- .../codegentest/HierrarchicalDataClassChild.java | 12 ++-- .../codegentest/ParcelAllTheThingsDataClass.java | 12 ++-- .../com/android/codegentest/SampleDataClass.java | 20 +++--- .../codegentest/SampleWithCustomBuilder.java | 12 ++-- .../StaleDataclassDetectorFalsePositivesTest.java | 6 +- .../codegen/src/com/android/codegen/Generators.kt | 12 ++-- .../src/com/android/codegen/SharedConstants.kt | 2 +- 10 files changed, 96 insertions(+), 70 deletions(-) (limited to 'tools/codegen') diff --git a/api/current.txt b/api/current.txt index b17667531b04..052a00c6d0e2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4481,7 +4481,7 @@ package android.app { method @IntRange(from=0) public int getNotingUid(); method @NonNull public String getOp(); method @IntRange(from=0) public long getTime(); - method public void writeToParcel(android.os.Parcel, int); + method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java index df6533a340f2..241895c9ff64 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -66,14 +66,18 @@ public final class AsyncNotedAppOp implements Parcelable { - // Code below generated by codegen v1.0.0. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AsyncNotedAppOp.java // - // CHECKSTYLE:OFF Generated code + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + /** * Creates a new AsyncNotedAppOp. @@ -83,7 +87,8 @@ public final class AsyncNotedAppOp implements Parcelable { * @param notingUid * Uid that noted the op * @param notingPackageName - * Package that noted the op + * Package that noted the op. {@code null} if the package name that noted the op could be not + * be determined (e.g. when the op is noted from native code). * @param message * Message associated with the noteOp. This message is set by the app noting the op * @param time @@ -127,7 +132,8 @@ public final class AsyncNotedAppOp implements Parcelable { } /** - * Package that noted the op + * Package that noted the op. {@code null} if the package name that noted the op could be not + * be determined (e.g. when the op is noted from native code). */ @DataClass.Generated.Member public @Nullable String getNotingPackageName() { @@ -152,7 +158,7 @@ public final class AsyncNotedAppOp implements Parcelable { @Override @DataClass.Generated.Member - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // You can override field equality logic by defining either of the methods like: // boolean fieldNameEquals(AsyncNotedAppOp other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } @@ -187,7 +193,7 @@ public final class AsyncNotedAppOp implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -205,6 +211,41 @@ public final class AsyncNotedAppOp implements Parcelable { @DataClass.Generated.Member public int describeContents() { return 0; } + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ AsyncNotedAppOp(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int opCode = in.readInt(); + int notingUid = in.readInt(); + String notingPackageName = (flg & 0x4) == 0 ? null : in.readString(); + String message = in.readString(); + long time = in.readLong(); + + this.mOpCode = opCode; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mOpCode, + "from", 0, + "to", AppOpsManager._NUM_OP - 1); + this.mNotingUid = notingUid; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mNotingUid, + "from", 0); + this.mNotingPackageName = notingPackageName; + this.mMessage = message; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mMessage); + this.mTime = time; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mTime, + "from", 0); + + // onConstructed(); // You can define this method to get a callback + } + @DataClass.Generated.Member public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -214,29 +255,14 @@ public final class AsyncNotedAppOp implements Parcelable { } @Override - @SuppressWarnings({"unchecked", "RedundantCast"}) - public AsyncNotedAppOp createFromParcel(android.os.Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - byte flg = in.readByte(); - int opCode = in.readInt(); - int notingUid = in.readInt(); - String notingPackageName = (flg & 0x4) == 0 ? null : in.readString(); - String message = in.readString(); - long time = in.readLong(); - return new AsyncNotedAppOp( - opCode, - notingUid, - notingPackageName, - message, - time); + public AsyncNotedAppOp createFromParcel(@NonNull android.os.Parcel in) { + return new AsyncNotedAppOp(in); } }; @DataClass.Generated( - time = 1566503083973L, - codegenVersion = "1.0.0", + time = 1571246617363L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java", inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 35150538d3d1..325c1c09dd8c 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -58,7 +58,7 @@ public class HierrarchicalDataClassBase implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(android.os.Parcel dest, int flags) { + public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -72,7 +72,7 @@ public class HierrarchicalDataClassBase implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected HierrarchicalDataClassBase(android.os.Parcel in) { + protected HierrarchicalDataClassBase(@android.annotation.NonNull android.os.Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -92,14 +92,14 @@ public class HierrarchicalDataClassBase implements Parcelable { } @Override - public HierrarchicalDataClassBase createFromParcel(android.os.Parcel in) { + public HierrarchicalDataClassBase createFromParcel(@android.annotation.NonNull android.os.Parcel in) { return new HierrarchicalDataClassBase(in); } }; @DataClass.Generated( - time = 1570828332402L, - codegenVersion = "1.0.8", + time = 1571258914826L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index c86740926813..6c92009f8533 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -74,7 +74,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { @Override @DataClass.Generated.Member - public void writeToParcel(android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -90,7 +90,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected HierrarchicalDataClassChild(android.os.Parcel in) { + protected HierrarchicalDataClassChild(@NonNull android.os.Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -114,14 +114,14 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { } @Override - public HierrarchicalDataClassChild createFromParcel(android.os.Parcel in) { + public HierrarchicalDataClassChild createFromParcel(@NonNull android.os.Parcel in) { return new HierrarchicalDataClassChild(in); } }; @DataClass.Generated( - time = 1570828333399L, - codegenVersion = "1.0.8", + time = 1571258915848L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 8d097a0c978e..36def8a8dfb1 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -52,7 +52,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -161,7 +161,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -185,7 +185,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected ParcelAllTheThingsDataClass(Parcel in) { + protected ParcelAllTheThingsDataClass(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -237,7 +237,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @Override - public ParcelAllTheThingsDataClass createFromParcel(Parcel in) { + public ParcelAllTheThingsDataClass createFromParcel(@NonNull Parcel in) { return new ParcelAllTheThingsDataClass(in); } }; @@ -410,8 +410,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1570828331396L, - codegenVersion = "1.0.8", + time = 1571258913802L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index d014d6d27ce8..c444d61a0fba 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1119,7 +1119,7 @@ public final class SampleDataClass implements Parcelable { @Override @DataClass.Generated.Member - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // You can override field equality logic by defining either of the methods like: // boolean fieldNameEquals(SampleDataClass other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } @@ -1184,8 +1184,8 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member void forEachField( - DataClass.PerIntFieldAction actionInt, - DataClass.PerObjectFieldAction actionObject) { + @NonNull DataClass.PerIntFieldAction actionInt, + @NonNull DataClass.PerObjectFieldAction actionObject) { actionInt.acceptInt(this, "num", mNum); actionInt.acceptInt(this, "num2", mNum2); actionInt.acceptInt(this, "num4", mNum4); @@ -1211,7 +1211,7 @@ public final class SampleDataClass implements Parcelable { /** @deprecated May cause boxing allocations - use with caution! */ @Deprecated @DataClass.Generated.Member - void forEachField(DataClass.PerObjectFieldAction action) { + void forEachField(@NonNull DataClass.PerObjectFieldAction action) { action.acceptObject(this, "num", mNum); action.acceptObject(this, "num2", mNum2); action.acceptObject(this, "num4", mNum4); @@ -1258,7 +1258,7 @@ public final class SampleDataClass implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -1297,7 +1297,7 @@ public final class SampleDataClass implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ SampleDataClass(Parcel in) { + /* package-private */ SampleDataClass(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -1420,7 +1420,7 @@ public final class SampleDataClass implements Parcelable { } @Override - public SampleDataClass createFromParcel(Parcel in) { + public SampleDataClass createFromParcel(@NonNull Parcel in) { return new SampleDataClass(in); } }; @@ -1872,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1570828329319L, - codegenVersion = "1.0.8", + time = 1571258911688L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 1c87e8f0ab36..55feae7200ea 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -142,7 +142,7 @@ public class SampleWithCustomBuilder implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -158,7 +158,7 @@ public class SampleWithCustomBuilder implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected SampleWithCustomBuilder(Parcel in) { + protected SampleWithCustomBuilder(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -184,7 +184,7 @@ public class SampleWithCustomBuilder implements Parcelable { } @Override - public SampleWithCustomBuilder createFromParcel(Parcel in) { + public SampleWithCustomBuilder createFromParcel(@NonNull Parcel in) { return new SampleWithCustomBuilder(in); } }; @@ -253,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1570828330331L, - codegenVersion = "1.0.8", + time = 1571258912752L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 27af37fe23ab..b967f19f9f7e 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.8. + // Code below generated by codegen v1.0.9. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,8 +65,8 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1570828334384L, - codegenVersion = "1.0.8", + time = 1571258916868L, + codegenVersion = "1.0.9", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 0ebb3cf2a447..431f378a8811 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -417,7 +417,7 @@ fun ClassPrinter.generateParcelable() { if (!isMethodGenerationSuppressed("writeToParcel", Parcel, "int")) { +"@Override" +GENERATED_MEMBER_HEADER - "public void writeToParcel($Parcel dest, int flags)" { + "public void writeToParcel(@$NonNull $Parcel dest, int flags)" { +"// You can override field parcelling by defining methods like:" +"// void parcelFieldName(Parcel dest, int flags) { ... }" +"" @@ -473,7 +473,7 @@ fun ClassPrinter.generateParcelable() { +"/** @hide */" +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})" +GENERATED_MEMBER_HEADER - "$visibility $ClassName($Parcel in) {" { + "$visibility $ClassName(@$NonNull $Parcel in) {" { +"// You can override field unparcelling by defining methods like:" +"// static FieldType unparcelFieldName(Parcel in) { ... }" +"" @@ -598,7 +598,7 @@ fun ClassPrinter.generateParcelable() { } +"@Override" - "public $ClassName createFromParcel($Parcel in)" { + "public $ClassName createFromParcel(@$NonNull $Parcel in)" { +"return new $ClassName(in);" } rmEmptyLine() @@ -611,7 +611,7 @@ fun ClassPrinter.generateEqualsHashcode() { if (!isMethodGenerationSuppressed("equals", "Object")) { +"@Override" +GENERATED_MEMBER_HEADER - "public boolean equals(Object o)" { + "public boolean equals(@$Nullable Object o)" { +"// You can override field equality logic by defining either of the methods like:" +"// boolean fieldNameEquals($ClassName other) { ... }" +"// boolean fieldNameEquals(FieldType otherValue) { ... }" @@ -904,7 +904,7 @@ fun ClassPrinter.generateForEachField() { usedSpecializationsSet.toList().forEachLastAware { specType, isLast -> val SpecType = specType.capitalize() val ActionClass = classRef("com.android.internal.util.DataClass.Per${SpecType}FieldAction") - +"$ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}" + +"@$NonNull $ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}" } }; " {" { usedSpecializations.forEachIndexed { i, specType -> @@ -919,7 +919,7 @@ fun ClassPrinter.generateForEachField() { +"/** @deprecated May cause boxing allocations - use with caution! */" +"@Deprecated" +GENERATED_MEMBER_HEADER - "void forEachField($PerObjectFieldAction<$ClassType> action)" { + "void forEachField(@$NonNull $PerObjectFieldAction<$ClassType> action)" { fields.forEachApply { +"action.acceptObject(this, \"$nameLowerCamel\", $name);" } diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 8c4583f68c4b..3eb9e7bb68c6 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.8" +const val CODEGEN_VERSION = "1.0.9" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From f51f6c0d91500ab5873f35dbe293b2176709f177 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 24 Oct 2019 15:01:38 -0700 Subject: Add CDM API to filter by BSSID bitmask Per recommendation from wifi team, this is an important filter to provide Test: manual Fixes: 143313206 Change-Id: Ifcad0f6ce5134854114aa9df97b2c44ece858a30 --- api/current.txt | 6 +- core/java/android/companion/WifiDeviceFilter.java | 287 +++++++++++++++++---- .../codegen/src/com/android/codegen/Generators.kt | 6 +- .../src/com/android/codegen/SharedConstants.kt | 2 +- 4 files changed, 249 insertions(+), 52 deletions(-) (limited to 'tools/codegen') diff --git a/api/current.txt b/api/current.txt index 71dc58b70021..5d532d428055 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9230,14 +9230,16 @@ package android.companion { public final class WifiDeviceFilter implements android.companion.DeviceFilter { method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); + method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } public static final class WifiDeviceFilter.Builder { ctor public WifiDeviceFilter.Builder(); method @NonNull public android.companion.WifiDeviceFilter build(); - method public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern); + method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@Nullable android.net.MacAddress); + method @NonNull public android.companion.WifiDeviceFilter.Builder setBssidMask(@NonNull android.net.MacAddress); + method @NonNull public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern); } } diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java index 62098d556e5b..58bf874efb41 100644 --- a/core/java/android/companion/WifiDeviceFilter.java +++ b/core/java/android/companion/WifiDeviceFilter.java @@ -17,17 +17,18 @@ package android.companion; import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal; -import static android.companion.BluetoothDeviceFilterUtils.patternFromString; -import static android.companion.BluetoothDeviceFilterUtils.patternToString; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.ScanFilter; +import android.net.MacAddress; import android.net.wifi.ScanResult; import android.os.Parcel; -import android.provider.OneTimeUseBuilder; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; import java.util.Objects; import java.util.regex.Pattern; @@ -37,30 +38,38 @@ import java.util.regex.Pattern; * * @see ScanFilter */ +@DataClass( + genParcelable = true, + genAidl = false, + genBuilder = true, + genEqualsHashCode = true, + genHiddenGetters = true) public final class WifiDeviceFilter implements DeviceFilter { - private final Pattern mNamePattern; - - private WifiDeviceFilter(Pattern namePattern) { - mNamePattern = namePattern; - } - - @SuppressLint("ParcelClassLoader") - private WifiDeviceFilter(Parcel in) { - this(patternFromString(in.readString())); - } + /** + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown + */ + @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class) + private @Nullable Pattern mNamePattern = null; - /** @hide */ - @Nullable - public Pattern getNamePattern() { - return mNamePattern; - } + /** + * If set, only devices with BSSID matching the given one will be shown + */ + private @Nullable MacAddress mBssid = null; + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + */ + private @NonNull MacAddress mBssidMask = MacAddress.BROADCAST_ADDRESS; /** @hide */ @Override public boolean matches(ScanResult device) { - return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device); + return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device) + && (mBssid == null + || MacAddress.fromString(device.BSSID).matches(mBssid, mBssidMask)); } /** @hide */ @@ -75,65 +84,249 @@ public final class WifiDeviceFilter implements DeviceFilter { return MEDIUM_TYPE_WIFI; } + + + // Code below generated by codegen v1.0.11. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/WifiDeviceFilter.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ WifiDeviceFilter( + @Nullable Pattern namePattern, + @Nullable MacAddress bssid, + @NonNull MacAddress bssidMask) { + this.mNamePattern = namePattern; + this.mBssid = bssid; + this.mBssidMask = bssidMask; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBssidMask); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable Pattern getNamePattern() { + return mNamePattern; + } + + /** + * If set, only devices with BSSID matching the given one will be shown + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable MacAddress getBssid() { + return mBssid; + } + + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull MacAddress getBssidMask() { + return mBssidMask; + } + @Override - public boolean equals(Object o) { + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(WifiDeviceFilter other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") WifiDeviceFilter that = (WifiDeviceFilter) o; - return Objects.equals(mNamePattern, that.mNamePattern); + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mNamePattern, that.mNamePattern) + && Objects.equals(mBssid, that.mBssid) + && Objects.equals(mBssidMask, that.mBssidMask); } @Override + @DataClass.Generated.Member public int hashCode() { - return Objects.hash(mNamePattern); + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mNamePattern); + _hash = 31 * _hash + Objects.hashCode(mBssid); + _hash = 31 * _hash + Objects.hashCode(mBssidMask); + return _hash; + } + + @DataClass.Generated.Member + static Parcelling sParcellingForNamePattern = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForPattern.class); + static { + if (sParcellingForNamePattern == null) { + sParcellingForNamePattern = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForPattern()); + } } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(patternToString(getNamePattern())); + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mNamePattern != null) flg |= 0x1; + if (mBssid != null) flg |= 0x2; + dest.writeByte(flg); + sParcellingForNamePattern.parcel(mNamePattern, dest, flags); + if (mBssid != null) dest.writeTypedObject(mBssid, flags); + dest.writeTypedObject(mBssidMask, flags); } @Override - public int describeContents() { - return 0; + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ WifiDeviceFilter(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + Pattern namePattern = sParcellingForNamePattern.unparcel(in); + MacAddress bssid = (flg & 0x2) == 0 ? null : (MacAddress) in.readTypedObject(MacAddress.CREATOR); + MacAddress bssidMask = (MacAddress) in.readTypedObject(MacAddress.CREATOR); + + this.mNamePattern = namePattern; + this.mBssid = bssid; + this.mBssidMask = bssidMask; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBssidMask); + + // onConstructed(); // You can define this method to get a callback } - public static final @android.annotation.NonNull Creator CREATOR - = new Creator() { + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { @Override - public WifiDeviceFilter createFromParcel(Parcel in) { - return new WifiDeviceFilter(in); + public WifiDeviceFilter[] newArray(int size) { + return new WifiDeviceFilter[size]; } @Override - public WifiDeviceFilter[] newArray(int size) { - return new WifiDeviceFilter[size]; + public WifiDeviceFilter createFromParcel(@NonNull Parcel in) { + return new WifiDeviceFilter(in); } }; /** - * Builder for {@link WifiDeviceFilter} + * A builder for {@link WifiDeviceFilter} */ - public static final class Builder extends OneTimeUseBuilder { - private Pattern mNamePattern; + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @Nullable Pattern mNamePattern; + private @Nullable MacAddress mBssid; + private @NonNull MacAddress mBssidMask; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } /** - * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the - * given regular expression will be shown - * @return self for chaining + * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular + * expression will be shown */ - public Builder setNamePattern(@Nullable Pattern regex) { + @DataClass.Generated.Member + public @NonNull Builder setNamePattern(@Nullable Pattern value) { checkNotUsed(); - mNamePattern = regex; + mBuilderFieldsSet |= 0x1; + mNamePattern = value; return this; } - /** @inheritDoc */ - @Override - @NonNull - public WifiDeviceFilter build() { - markUsed(); - return new WifiDeviceFilter(mNamePattern); + /** + * If set, only devices with BSSID matching the given one will be shown + */ + @DataClass.Generated.Member + public @NonNull Builder setBssid(@Nullable MacAddress value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mBssid = value; + return this; + } + + /** + * If set, only bits at positions set in this mask, will be compared to the given + * {@link Builder#setBssid BSSID} filter. + */ + @DataClass.Generated.Member + public @NonNull Builder setBssidMask(@NonNull MacAddress value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mBssidMask = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull WifiDeviceFilter build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mNamePattern = null; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mBssid = null; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mBssidMask = MacAddress.BROADCAST_ADDRESS; + } + WifiDeviceFilter o = new WifiDeviceFilter( + mNamePattern, + mBssid, + mBssidMask); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x8) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } } } + + @DataClass.Generated( + time = 1571960300742L, + codegenVersion = "1.0.11", + sourceFile = "frameworks/base/core/java/android/companion/WifiDeviceFilter.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)") + @Deprecated + private void __metadata() {} + } diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 431f378a8811..bd32f9c6d9cd 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -212,13 +212,15 @@ fun ClassPrinter.generateBuilder() { "Object" } + val maybeFinal = if_(classAst.isFinal, "final ") + +"/**" +" * A builder for {@link $ClassName}" if (FeatureFlag.BUILDER.hidden) +" * @hide" +" */" +"@SuppressWarnings(\"WeakerAccess\")" +GENERATED_MEMBER_HEADER - !"public static class $BuilderClass$genericArgs" + !"public static ${maybeFinal}class $BuilderClass$genericArgs" if (BuilderSupertype != "Object") { appendSameLine(" extends $BuilderSupertype") } @@ -359,7 +361,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { private fun ClassPrinter.generateBuilderBuild() { +"/** Builds the instance. This builder should not be touched after calling this! */" - "public $ClassType build()" { + "public @$NonNull $ClassType build()" { +"checkNotUsed();" +"mBuilderFieldsSet |= ${bitAtExpr(fields.size)}; // Mark builder used" +"" diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 3eb9e7bb68c6..270d34a01a64 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.9" +const val CODEGEN_VERSION = "1.0.11" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From 322e8b17721a6956e00407e8d431ceecbd245b5c Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 22 Oct 2019 17:32:08 -0700 Subject: [codegen] Support nested classes Adds support for arbitrarily-nested @DataClasses Only static ones are supported for now See FileInfo for the main implementation piece Fixes: 139833958 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: I31cd16969788c47003a7a15a3573a4bf623ab960 --- tests/Codegen/runTest.sh | 1 + .../codegentest/HierrarchicalDataClassBase.java | 10 +- .../codegentest/HierrarchicalDataClassChild.java | 10 +- .../codegentest/ParcelAllTheThingsDataClass.java | 12 +- .../com/android/codegentest/SampleDataClass.java | 14 +- .../android/codegentest/SampleDataClassTest.java | 20 ++ .../codegentest/SampleWithCustomBuilder.java | 12 +- .../codegentest/SampleWithNestedDataClasses.java | 390 +++++++++++++++++++++ .../StaleDataclassDetectorFalsePositivesTest.java | 10 +- tools/codegen/src/com/android/codegen/ClassInfo.kt | 40 +-- .../src/com/android/codegen/ClassPrinter.kt | 282 +++------------ tools/codegen/src/com/android/codegen/FieldInfo.kt | 12 +- tools/codegen/src/com/android/codegen/FileInfo.kt | 289 +++++++++++++++ .../codegen/src/com/android/codegen/Generators.kt | 6 +- .../src/com/android/codegen/ImportsProvider.kt | 91 +++++ tools/codegen/src/com/android/codegen/Main.kt | 77 +--- tools/codegen/src/com/android/codegen/Printer.kt | 186 ++++++++++ tools/codegen/src/com/android/codegen/Utils.kt | 52 +++ .../staledataclass/StaleDataclassProcessor.kt | 11 +- 19 files changed, 1162 insertions(+), 363 deletions(-) create mode 100644 tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java create mode 100644 tools/codegen/src/com/android/codegen/FileInfo.kt create mode 100644 tools/codegen/src/com/android/codegen/ImportsProvider.kt create mode 100644 tools/codegen/src/com/android/codegen/Printer.kt (limited to 'tools/codegen') diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index 929f122e261e..31ab6d2ba46a 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -17,6 +17,7 @@ else header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java && \ ( cd $ANDROID_BUILD_TOP && header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 325c1c09dd8c..56ad217e6d19 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -98,11 +98,15 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1571258914826L, - codegenVersion = "1.0.9", + time = 1572630437620L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 6c92009f8533..59e07c471d63 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,11 +120,15 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1571258915848L, - codegenVersion = "1.0.9", + time = 1572630438646L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 36def8a8dfb1..3c00a3a63eca 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -52,7 +52,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -361,7 +361,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } /** Builds the instance. This builder should not be touched after calling this! */ - public ParcelAllTheThingsDataClass build() { + public @NonNull ParcelAllTheThingsDataClass build() { checkNotUsed(); mBuilderFieldsSet |= 0x100; // Mark builder used @@ -410,11 +410,15 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1571258913802L, - codegenVersion = "1.0.9", + time = 1572630436563L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index c444d61a0fba..9765bdc261db 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1430,7 +1430,7 @@ public final class SampleDataClass implements Parcelable { */ @SuppressWarnings("WeakerAccess") @DataClass.Generated.Member - public static class Builder { + public static final class Builder { private int mNum; private int mNum2; @@ -1793,7 +1793,7 @@ public final class SampleDataClass implements Parcelable { } /** Builds the instance. This builder should not be touched after calling this! */ - public SampleDataClass build() { + public @NonNull SampleDataClass build() { checkNotUsed(); mBuilderFieldsSet |= 0x100000; // Mark builder used @@ -1872,11 +1872,15 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1571258911688L, - codegenVersion = "1.0.9", + time = 1572630434434L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java index c7a773530963..d13257743e21 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java @@ -243,6 +243,26 @@ public class SampleDataClassTest { assertEquals(instance.toString(), unparceledInstance.toString()); } + @Test + public void testNestedDataClasses_notMangledWhenParceled() { + assertEqualsAfterParcelling( + new SampleWithNestedDataClasses.NestedDataClass("1"), + SampleWithNestedDataClasses.NestedDataClass.CREATOR); + + assertEqualsAfterParcelling( + new SampleWithNestedDataClasses.NestedDataClass2("2"), + SampleWithNestedDataClasses.NestedDataClass2.CREATOR); + + assertEqualsAfterParcelling( + new SampleWithNestedDataClasses.NestedDataClass2.NestedDataClass3(3), + SampleWithNestedDataClasses.NestedDataClass2.NestedDataClass3.CREATOR); + } + + private static void assertEqualsAfterParcelling( + T p, Parcelable.Creator creator) { + assertEquals(p, parcelAndUnparcel(p, creator)); + } + private static T parcelAndUnparcel( T original, Parcelable.Creator creator) { Parcel p = Parcel.obtain(); diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 55feae7200ea..c62f2b8f6acb 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -224,7 +224,7 @@ public class SampleWithCustomBuilder implements Parcelable { } /** Builds the instance. This builder should not be touched after calling this! */ - public SampleWithCustomBuilder build() { + public @NonNull SampleWithCustomBuilder build() { checkNotUsed(); mBuilderFieldsSet |= 0x8; // Mark builder used @@ -253,11 +253,15 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1571258912752L, - codegenVersion = "1.0.9", + time = 1572630435484L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java new file mode 100644 index 000000000000..5b93b38161e3 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2019 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 com.android.codegentest; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * An example of deeply nested data classes + */ +public class SampleWithNestedDataClasses { + + int mFoo = 0; + + @DataClass(genEqualsHashCode = true) + public static class NestedDataClass implements Parcelable { + + @NonNull String mBar; + + + + // Code below generated by codegen v1.0.11. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public NestedDataClass( + @NonNull String bar) { + this.mBar = bar; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBar); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull String getBar() { + return mBar; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(NestedDataClass other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + NestedDataClass that = (NestedDataClass) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mBar, that.mBar); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mBar); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeString(mBar); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected NestedDataClass(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String bar = in.readString(); + + this.mBar = bar; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBar); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public NestedDataClass[] newArray(int size) { + return new NestedDataClass[size]; + } + + @Override + public NestedDataClass createFromParcel(@NonNull Parcel in) { + return new NestedDataClass(in); + } + }; + + @DataClass.Generated( + time = 1572630440713L, + codegenVersion = "1.0.11", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", + inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + + } + + @DataClass(genEqualsHashCode = true) + public static class NestedDataClass2 implements Parcelable { + + @NonNull String mBaz; + + @DataClass(genEqualsHashCode = true) + public static class NestedDataClass3 implements Parcelable { + + @NonNull long mBaz2; + + + + // Code below generated by codegen v1.0.11. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public NestedDataClass3( + @NonNull long baz2) { + this.mBaz2 = baz2; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBaz2); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull long getBaz2() { + return mBaz2; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(NestedDataClass3 other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + NestedDataClass3 that = (NestedDataClass3) o; + //noinspection PointlessBooleanExpression + return true + && mBaz2 == that.mBaz2; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Long.hashCode(mBaz2); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeLong(mBaz2); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected NestedDataClass3(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long baz2 = in.readLong(); + + this.mBaz2 = baz2; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBaz2); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public NestedDataClass3[] newArray(int size) { + return new NestedDataClass3[size]; + } + + @Override + public NestedDataClass3 createFromParcel(@NonNull Parcel in) { + return new NestedDataClass3(in); + } + }; + + @DataClass.Generated( + time = 1572630440724L, + codegenVersion = "1.0.11", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", + inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + + } + + + + // Code below generated by codegen v1.0.11. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public NestedDataClass2( + @NonNull String baz) { + this.mBaz = baz; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBaz); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull String getBaz() { + return mBaz; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(NestedDataClass2 other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + NestedDataClass2 that = (NestedDataClass2) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mBaz, that.mBaz); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mBaz); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeString(mBaz); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected NestedDataClass2(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String baz = in.readString(); + + this.mBaz = baz; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBaz); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public NestedDataClass2[] newArray(int size) { + return new NestedDataClass2[size]; + } + + @Override + public NestedDataClass2 createFromParcel(@NonNull Parcel in) { + return new NestedDataClass2(in); + } + }; + + @DataClass.Generated( + time = 1572630440729L, + codegenVersion = "1.0.11", + sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", + inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + + } + + void someCode() {} +} diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index b967f19f9f7e..2964451225e4 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.9. + // Code below generated by codegen v1.0.11. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,11 +65,15 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1571258916868L, - codegenVersion = "1.0.9", + time = 1572630439617L, + codegenVersion = "1.0.11", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated private void __metadata() {} + + //@formatter:on + // End of generated code + } diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt index 92da9dab863b..bf95a2eb2193 100644 --- a/tools/codegen/src/com/android/codegen/ClassInfo.kt +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -1,47 +1,15 @@ package com.android.codegen -import com.github.javaparser.ParseProblemException -import com.github.javaparser.ParseResult -import com.github.javaparser.ast.CompilationUnit import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration -open class ClassInfo(val sourceLines: List) { +open class ClassInfo(val classAst: ClassOrInterfaceDeclaration, val fileInfo: FileInfo) { - private val userSourceCode = (sourceLines + "}").joinToString("\n") - val fileAst: CompilationUnit = try { - JAVA_PARSER.parse(userSourceCode).throwIfFailed() - } catch (e: ParseProblemException) { - throw parseFailed(cause = e) - } - - fun ParseResult.throwIfFailed(): T { - if (problems.isNotEmpty()) { - throw parseFailed( - desc = this@throwIfFailed.problems.joinToString("\n"), - cause = this@throwIfFailed.problems.mapNotNull { it.cause.orElse(null) }.firstOrNull()) - } - return result.get() - } + val fileAst = fileInfo.fileAst - private fun parseFailed(cause: Throwable? = null, desc: String = ""): RuntimeException { - return RuntimeException("Failed to parse code:\n" + - userSourceCode - .lines() - .mapIndexed { lnNum, ln -> "/*$lnNum*/$ln" } - .joinToString("\n") + "\n$desc", - cause) - } - - val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration val nestedClasses = classAst.members.filterIsInstance() - val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration) - .implementedTypes.map { it.asString() } - - val superClass = run { - val superClasses = (fileAst.types[0] as ClassOrInterfaceDeclaration).extendedTypes - if (superClasses.isNonEmpty) superClasses[0] else null - } + val superInterfaces = classAst.implementedTypes.map { it.asString() } + val superClass = classAst.extendedTypes.getOrNull(0) val ClassName = classAst.nameAsString private val genericArgsAst = classAst.typeParameters diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index bd72d9e7ec21..a4fd374d0c6e 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -11,36 +11,12 @@ import com.github.javaparser.ast.type.ClassOrInterfaceType * [ClassInfo] + utilities for printing out new class code with proper indentation and imports */ class ClassPrinter( - source: List, - private val stringBuilder: StringBuilder, - var cliArgs: Array -) : ClassInfo(source) { + classAst: ClassOrInterfaceDeclaration, + fileInfo: FileInfo +) : ClassInfo(classAst, fileInfo), Printer, ImportsProvider { val GENERATED_MEMBER_HEADER by lazy { "@$GeneratedMember" } - // Imports - val NonNull by lazy { classRef("android.annotation.NonNull") } - val NonEmpty by lazy { classRef("android.annotation.NonEmpty") } - val Nullable by lazy { classRef("android.annotation.Nullable") } - val TextUtils by lazy { classRef("android.text.TextUtils") } - val LinkedHashMap by lazy { classRef("java.util.LinkedHashMap") } - val Collections by lazy { classRef("java.util.Collections") } - val Preconditions by lazy { classRef("com.android.internal.util.Preconditions") } - val ArrayList by lazy { classRef("java.util.ArrayList") } - val DataClass by lazy { classRef("com.android.internal.util.DataClass") } - val DataClassEnum by lazy { classRef("com.android.internal.util.DataClass.Enum") } - val ParcelWith by lazy { classRef("com.android.internal.util.DataClass.ParcelWith") } - 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 Parcel by lazy { classRef("android.os.Parcel") } - val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") } - init { val fieldsWithMissingNullablity = fields.filter { field -> !field.isPrimitive @@ -60,50 +36,61 @@ class ClassPrinter( } } - /** - * Optionally shortens a class reference if there's a corresponding import present - */ - fun classRef(fullName: String): String { - if (cliArgs.contains(FLAG_NO_FULL_QUALIFIERS)) { - return fullName.split(".").dropWhile { it[0].isLowerCase() }.joinToString(".") - } + val cliArgs get() = fileInfo.cliArgs - val pkg = fullName.substringBeforeLast(".") - val simpleName = fullName.substringAfterLast(".") - if (fileAst.imports.any { imprt -> - imprt.nameAsString == fullName - || (imprt.isAsterisk && imprt.nameAsString == pkg) - }) { - return simpleName - } else { - val outerClass = pkg.substringAfterLast(".", "") - if (outerClass.firstOrNull()?.isUpperCase() == true) { - return classRef(pkg) + "." + simpleName - } - } - return fullName - } + fun print() { + currentIndent = fileInfo.sourceLines + .find { "class $ClassName" in it }!! + .takeWhile { it.isWhitespace() } + .plus(INDENT_SINGLE) - /** @see classRef */ - inline fun classRef(): String { - return classRef(T::class.java.name) - } + +fileInfo.generatedWarning - /** @see classRef */ - fun memberRef(fullName: String): String { - val className = fullName.substringBeforeLast(".") - val methodName = fullName.substringAfterLast(".") - return if (fileAst.imports.any { - it.isStatic - && (it.nameAsString == fullName - || (it.isAsterisk && it.nameAsString == className)) - }) { - className.substringAfterLast(".") + "." + methodName - } else { - classRef(className) + "." + methodName + if (FeatureFlag.CONST_DEFS()) generateConstDefs() + + + if (FeatureFlag.CONSTRUCTOR()) { + generateConstructor("public") + } else if (FeatureFlag.BUILDER() + || FeatureFlag.COPY_CONSTRUCTOR() + || FeatureFlag.WITHERS()) { + generateConstructor("/* package-private */") } + if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() + + if (FeatureFlag.GETTERS()) generateGetters() + if (FeatureFlag.SETTERS()) generateSetters() + if (FeatureFlag.TO_STRING()) generateToString() + if (FeatureFlag.EQUALS_HASH_CODE()) generateEqualsHashcode() + + if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField() + + if (FeatureFlag.WITHERS()) generateWithers() + + if (FeatureFlag.PARCELABLE()) generateParcelable() + + if (FeatureFlag.BUILDER() && FeatureFlag.BUILD_UPON()) generateBuildUpon() + if (FeatureFlag.BUILDER()) generateBuilder() + + if (FeatureFlag.AIDL()) fileInfo.generateAidl() //TODO guard against nested classes requesting aidl + + generateMetadata(fileInfo.file) + + +""" + //@formatter:on + $GENERATED_END + + """ + + rmEmptyLine() } + override var currentIndent: String + get() = fileInfo.currentIndent + set(value) { fileInfo.currentIndent = value } + override val stringBuilder get() = fileInfo.stringBuilder + + val dataClassAnnotationFeatures = classAst.annotations .find { it.nameAsString == DataClass } ?.let { it as? NormalAnnotationExpr } @@ -143,7 +130,7 @@ class ClassPrinter( || onByDefault FeatureFlag.CONSTRUCTOR -> !FeatureFlag.BUILDER() FeatureFlag.PARCELABLE -> "Parcelable" in superInterfaces - FeatureFlag.AIDL -> FeatureFlag.PARCELABLE() + FeatureFlag.AIDL -> fileInfo.mainClass.nameAsString == ClassName && FeatureFlag.PARCELABLE() FeatureFlag.IMPLICIT_NONNULL -> fields.any { it.isNullable } && fields.none { "@$NonNull" in it.annotations } else -> onByDefault @@ -163,162 +150,7 @@ class ClassPrinter( } } - var currentIndent = INDENT_SINGLE - private set - - fun pushIndent() { - currentIndent += INDENT_SINGLE - } - - fun popIndent() { - currentIndent = currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length) - } - - fun backspace() = stringBuilder.setLength(stringBuilder.length - 1) - val lastChar get() = stringBuilder[stringBuilder.length - 1] - - private fun appendRaw(s: String) { - stringBuilder.append(s) - } - - fun append(s: String) { - if (s.isBlank() && s != "\n") { - appendRaw(s) - } else { - appendRaw(s.lines().map { line -> - if (line.startsWith(" *")) line else line.trimStart() - }.joinToString("\n$currentIndent")) - } - } - - fun appendSameLine(s: String) { - while (lastChar.isWhitespace() || lastChar.isNewline()) { - backspace() - } - appendRaw(s) - } - - fun rmEmptyLine() { - while (lastChar.isWhitespaceNonNewline()) backspace() - if (lastChar.isNewline()) backspace() - } - - /** - * Syntactic sugar for: - * ``` - * +"code()"; - * ``` - * to append the given string plus a newline - */ - operator fun String.unaryPlus() = append("$this\n") - - /** - * Syntactic sugar for: - * ``` - * !"code()"; - * ``` - * to append the given string without a newline - */ - operator fun String.not() = append(this) - - /** - * Syntactic sugar for: - * ``` - * ... { - * ... - * }+";" - * ``` - * to append a ';' on same line after a block, and a newline afterwards - */ - operator fun Unit.plus(s: String) { - appendSameLine(s) - +"" - } - - /** - * A multi-purpose syntactic sugar for appending the given string plus anything generated in - * the given [block], the latter with the appropriate deeper indent, - * and resetting the indent back to original at the end - * - * Usage examples: - * - * ``` - * "if (...)" { - * ... - * } - * ``` - * to append a corresponding if block appropriate indentation - * - * ``` - * "void foo(...)" { - * ... - * } - * ``` - * similar to the previous one, plus an extra empty line after the function body - * - * ``` - * "void foo(" { - * - * } - * ``` - * to use proper indentation for args code and close the bracket on same line at end - * - * ``` - * "..." { - * ... - * } - * to use the correct indentation for inner code, resetting it at the end - */ - inline operator fun String.invoke(block: ClassPrinter.() -> Unit) { - if (this == " {") { - appendSameLine(this) - } else { - append(this) - } - when { - endsWith("(") -> { - indentedBy(2, block) - appendSameLine(")") - } - endsWith("{") || endsWith(")") -> { - if (!endsWith("{")) appendSameLine(" {") - indentedBy(1, block) - +"}" - if ((endsWith(") {") || endsWith(")") || this == " {") - && !startsWith("synchronized") - && !startsWith("switch") - && !startsWith("if ") - && !contains(" else ") - && !contains("new ") - && !contains("return ")) { - +"" // extra line after function definitions - } - } - else -> indentedBy(2, block) - } - } - - inline fun indentedBy(level: Int, block: ClassPrinter.() -> Unit) { - append("\n") - level times { - append(INDENT_SINGLE) - pushIndent() - } - block() - level times { popIndent() } - rmEmptyLine() - +"" - } - inline fun Iterable.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { - forEachApply { - b() - if (isLast) { - while (lastChar == ' ' || lastChar == '\n') backspace() - if (lastChar == ',') backspace() - } - } - } inline operator fun invoke(f: ClassPrinter.() -> R): R = run(f) @@ -381,10 +213,10 @@ class ClassPrinter( BuilderClass = (builderFactoryOverride.type as ClassOrInterfaceType).nameAsString BuilderType = builderFactoryOverride.type.asString() } else { - val builderExtension = (fileAst.types - + classAst.childNodes.filterIsInstance(TypeDeclaration::class.java)).find { - it.nameAsString == CANONICAL_BUILDER_CLASS - } + val builderExtension = classAst + .childNodes + .filterIsInstance(TypeDeclaration::class.java) + .find { it.nameAsString == CANONICAL_BUILDER_CLASS } if (builderExtension != null) { BuilderClass = BASE_BUILDER_CLASS val tp = (builderExtension as ClassOrInterfaceDeclaration).typeParameters diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index 1a7fd6e241aa..ed35a1dfc599 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -1,5 +1,6 @@ package com.android.codegen +import com.github.javaparser.JavaParser import com.github.javaparser.ast.body.FieldDeclaration import com.github.javaparser.ast.expr.ClassExpr import com.github.javaparser.ast.expr.Name @@ -111,11 +112,12 @@ data class FieldInfo( val annotations by lazy { if (FieldClass in BUILTIN_SPECIAL_PARCELLINGS) { classPrinter { - fieldAst.addAnnotation(SingleMemberAnnotationExpr( - Name(ParcelWith), - ClassExpr(JAVA_PARSER - .parseClassOrInterfaceType("$Parcelling.BuiltIn.For$FieldClass") - .throwIfFailed()))) + fileInfo.apply { + fieldAst.addAnnotation(SingleMemberAnnotationExpr( + Name(ParcelWith), + ClassExpr(parseJava(JavaParser::parseClassOrInterfaceType, + "$Parcelling.BuiltIn.For$FieldClass")))) + } } } fieldAst.annotations.map { it.removeComment().toString() } diff --git a/tools/codegen/src/com/android/codegen/FileInfo.kt b/tools/codegen/src/com/android/codegen/FileInfo.kt new file mode 100644 index 000000000000..9c15fbf84223 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/FileInfo.kt @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2019 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 com.android.codegen + +import com.github.javaparser.JavaParser +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration +import com.github.javaparser.ast.body.TypeDeclaration +import java.io.File + +/** + * File-level parsing & printing logic + * + * @see [main] entrypoint + */ +class FileInfo( + val sourceLines: List, + val cliArgs: Array, + val file: File) + : Printer, ImportsProvider { + + override val fileAst: CompilationUnit + = parseJava(JavaParser::parse, sourceLines.joinToString("\n")) + + override val stringBuilder = StringBuilder() + override var currentIndent = INDENT_SINGLE + + + val generatedWarning = run { + val fileEscaped = file.absolutePath.replace( + System.getenv("ANDROID_BUILD_TOP"), "\$ANDROID_BUILD_TOP") + + """ + + + // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ $THIS_SCRIPT_LOCATION$CODEGEN_NAME ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + """ + } + private val generatedWarningNumPrecedingEmptyLines + = generatedWarning.lines().takeWhile { it.isBlank() }.size + + val classes = fileAst.types + .filterIsInstance() + .flatMap { it.plusNested() } + .filterNot { it.isInterface } + + val mainClass = classes.find { it.nameAsString == file.nameWithoutExtension }!! + + // Parse stage 1 + val classBounds: List = classes.map { ast -> + ClassBounds(ast, fileInfo = this) + }.apply { + forEachApply { + if (ast.isNestedType) { + val parent = find { + it.name == (ast.parentNode.get()!! as TypeDeclaration<*>).nameAsString + }!! + parent.nested.add(this) + nestedIn = parent + } + } + } + + // Parse Stage 2 + var codeChunks = buildList { + val mainClassBounds = classBounds.find { it.nestedIn == null }!! + add(CodeChunk.FileHeader( + mainClassBounds.fileInfo.sourceLines.subList(0, mainClassBounds.range.start))) + add(CodeChunk.DataClass.parse(mainClassBounds)) + } + + // Output stage + fun main() { + codeChunks.forEach { print(it) } + } + + fun print(chunk: CodeChunk) { + when(chunk) { + is CodeChunk.GeneratedCode -> { + // Re-parse class code, discarding generated code and nested dataclasses + val ast = chunk.owner.chunks + .filter { + it.javaClass == CodeChunk.Code::class.java + || it.javaClass == CodeChunk.ClosingBrace::class.java + } + .flatMap { (it as CodeChunk.Code).lines } + .joinToString("\n") + .let { + parseJava(JavaParser::parseTypeDeclaration, it) + as ClassOrInterfaceDeclaration + } + + // Write new generated code + ClassPrinter(ast, fileInfo = this).print() + } + is CodeChunk.ClosingBrace -> { + // Special case - print closing brace with -1 indent + rmEmptyLine() + popIndent() + +"\n}" + } + // Print general code as-is + is CodeChunk.Code -> chunk.lines.forEach { stringBuilder.appendln(it) } + // Recursively render data classes + is CodeChunk.DataClass -> chunk.chunks.forEach { print(it) } + } + } + + /** + * Output of stage 1 of parsing a file: + * Recursively nested ranges of code line numbers containing nested classes + */ + data class ClassBounds( + val ast: ClassOrInterfaceDeclaration, + val fileInfo: FileInfo, + val name: String = ast.nameAsString, + val range: ClosedRange = ast.range.get()!!.let { rng -> rng.begin.line-1..rng.end.line-1 }, + val nested: MutableList = mutableListOf(), + var nestedIn: ClassBounds? = null) { + + val nestedDataClasses: List by lazy { + nested.filter { it.isDataclass }.sortedBy { it.range.start } + } + val isDataclass = ast.annotations.any { it.nameAsString.endsWith("DataClass") } + + val baseIndentLength = fileInfo.sourceLines.find { "class $name" in it }!!.takeWhile { it == ' ' }.length + val baseIndent = buildString { repeat(baseIndentLength) { append(' ') } } + + val sourceNoPrefix = fileInfo.sourceLines.drop(range.start) + val generatedCodeRange = sourceNoPrefix + .indexOfFirst { it.startsWith("$baseIndent$INDENT_SINGLE// $GENERATED_WARNING_PREFIX") } + .let { start -> + if (start < 0) { + null + } else { + var endInclusive = sourceNoPrefix.indexOfFirst { + it.startsWith("$baseIndent$INDENT_SINGLE$GENERATED_END") + } + if (endInclusive == -1) { + // Legacy generated code doesn't have end markers + endInclusive = fileInfo.sourceLines.size - 2 + } + IntRange( + range.start + start - fileInfo.generatedWarningNumPrecedingEmptyLines, + range.start + endInclusive) + } + } + + /** Debug info */ + override fun toString(): String { + return buildString { + appendln("class $name $range") + nested.forEach { + appendln(it) + } + appendln("end $name") + } + } + } + + /** + * Output of stage 2 of parsing a file + */ + sealed class CodeChunk { + /** General code */ + open class Code(val lines: List): CodeChunk() {} + + /** Copyright + package + imports + main javadoc */ + class FileHeader(lines: List): Code(lines) + + /** Code to be discarded and refreshed */ + open class GeneratedCode(lines: List): Code(lines) { + lateinit var owner: DataClass + + class Placeholder: GeneratedCode(emptyList()) + } + + object ClosingBrace: Code(listOf("}")) + + data class DataClass( + val ast: ClassOrInterfaceDeclaration, + val chunks: List, + val generatedCode: GeneratedCode?): CodeChunk() { + + companion object { + fun parse(classBounds: ClassBounds): DataClass { + val initial = Code(lines = classBounds.fileInfo.sourceLines.subList( + fromIndex = classBounds.range.start, + toIndex = findLowerBound( + thisClass = classBounds, + nextNestedClass = classBounds.nestedDataClasses.getOrNull(0)))) + + val chunks = mutableListOf(initial) + + classBounds.nestedDataClasses.forEachSequentialPair { + nestedDataClass, nextNestedDataClass -> + chunks += DataClass.parse(nestedDataClass) + chunks += Code(lines = classBounds.fileInfo.sourceLines.subList( + fromIndex = nestedDataClass.range.endInclusive + 1, + toIndex = findLowerBound( + thisClass = classBounds, + nextNestedClass = nextNestedDataClass))) + } + + var generatedCode = classBounds.generatedCodeRange?.let { rng -> + GeneratedCode(classBounds.fileInfo.sourceLines.subList( + rng.start, rng.endInclusive+1)) + } + if (generatedCode != null) { + chunks += generatedCode + chunks += ClosingBrace + } else if (classBounds.isDataclass) { + + // Insert placeholder for generated code to be inserted for the 1st time + chunks.last = (chunks.last as Code) + .lines + .dropLastWhile { it.isBlank() } + .run { + if (last().dropWhile { it.isWhitespace() }.startsWith("}")) { + dropLast(1) + } else { + this + } + }.let { Code(it) } + generatedCode = GeneratedCode.Placeholder() + chunks += generatedCode + chunks += ClosingBrace + } else { + // Outer class may be not a @DataClass but contain ones + // so just skip generated code for them + } + + return DataClass(classBounds.ast, chunks, generatedCode).also { + generatedCode?.owner = it + } + } + + private fun findLowerBound(thisClass: ClassBounds, nextNestedClass: ClassBounds?): Int { + return nextNestedClass?.range?.start + ?: thisClass.generatedCodeRange?.start + ?: thisClass.range.endInclusive + 1 + } + } + } + + /** Debug info */ + fun summary(): String = when(this) { + is Code -> "${javaClass.simpleName}(${lines.size} lines): ${lines.getOrNull(0)?.take(70) ?: ""}..." + is DataClass -> "DataClass ${ast.nameAsString}:\n" + + chunks.joinToString("\n") { it.summary() } + + "\n//end ${ast.nameAsString}" + } + } + + private fun ClassOrInterfaceDeclaration.plusNested(): List { + return mutableListOf().apply { + add(this@plusNested) + childNodes.filterIsInstance() + .flatMap { it.plusNested() } + .let { addAll(it) } + } + } +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index bd32f9c6d9cd..c25d0c74f251 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -119,14 +119,14 @@ fun ClassPrinter.generateConstDef(consts: List + imprt.nameAsString == fullName + || (imprt.isAsterisk && imprt.nameAsString == pkg) + }) { + return simpleName + } else { + val outerClass = pkg.substringAfterLast(".", "") + if (outerClass.firstOrNull()?.isUpperCase() == true) { + return classRef(pkg) + "." + simpleName + } + } + return fullName + } + + /** @see classRef */ + fun memberRef(fullName: String): String { + val className = fullName.substringBeforeLast(".") + val methodName = fullName.substringAfterLast(".") + return if (fileAst.imports.any { + it.isStatic + && (it.nameAsString == fullName + || (it.isAsterisk && it.nameAsString == className)) + }) { + className.substringAfterLast(".") + "." + methodName + } else { + classRef(className) + "." + methodName + } + } +} + +/** @see classRef */ +inline fun ImportsProvider.classRef(): String { + return classRef(T::class.java.name) +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index ce83d3dc8e51..4b508d022991 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -6,6 +6,7 @@ import java.io.File const val THIS_SCRIPT_LOCATION = "" const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" +const val GENERATED_END = "// End of generated code" const val INDENT_SINGLE = " " val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") @@ -115,81 +116,15 @@ fun main(args: Array) { System.exit(0) } val file = File(args.last()).absoluteFile - val sourceLinesNoClosingBrace = file.readLines().dropLastWhile { + val sourceLisnesOriginal = file.readLines() + val sourceLinesNoClosingBrace = sourceLisnesOriginal.dropLastWhile { it.startsWith("}") || it.all(Char::isWhitespace) } val cliArgs = handleUpdateFlag(args, sourceLinesNoClosingBrace) - val sourceLinesAsIs = discardGeneratedCode(sourceLinesNoClosingBrace) - val sourceLines = sourceLinesAsIs - .filterNot { it.trim().startsWith("//") } - .map { it.trimEnd().dropWhile { it == '\n' } } - val stringBuilder = StringBuilder(sourceLinesAsIs.joinToString("\n")) - ClassPrinter(sourceLines, stringBuilder, cliArgs).run { - - val cliExecutable = "$THIS_SCRIPT_LOCATION$CODEGEN_NAME" - val fileEscaped = file.absolutePath.replace( - System.getenv("ANDROID_BUILD_TOP"), "\$ANDROID_BUILD_TOP") - - - +""" - - - - // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - """ - - if (FeatureFlag.CONST_DEFS()) generateConstDefs() - - - if (FeatureFlag.CONSTRUCTOR()) { - generateConstructor("public") - } else if (FeatureFlag.BUILDER() - || FeatureFlag.COPY_CONSTRUCTOR() - || FeatureFlag.WITHERS()) { - generateConstructor("/* package-private */") - } - if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() - - if (FeatureFlag.GETTERS()) generateGetters() - if (FeatureFlag.SETTERS()) generateSetters() - if (FeatureFlag.TO_STRING()) generateToString() - if (FeatureFlag.EQUALS_HASH_CODE()) generateEqualsHashcode() - - if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField() - - if (FeatureFlag.WITHERS()) generateWithers() - - if (FeatureFlag.PARCELABLE()) generateParcelable() - - if (FeatureFlag.BUILDER() && FeatureFlag.BUILD_UPON()) generateBuildUpon() - if (FeatureFlag.BUILDER()) generateBuilder() - - if (FeatureFlag.AIDL()) generateAidl(file) - - generateMetadata(file) - - rmEmptyLine() - } - stringBuilder.append("\n}\n") - file.writeText(stringBuilder.toString().mapLines { trimEnd() }) -} - -internal fun discardGeneratedCode(sourceLinesNoClosingBrace: List): List { - return sourceLinesNoClosingBrace - .takeWhile { GENERATED_WARNING_PREFIX !in it } - .dropLastWhile(String::isBlank) + val fileInfo = FileInfo(sourceLisnesOriginal, cliArgs, file) + fileInfo.main() + file.writeText(fileInfo.stringBuilder.toString().mapLines { trimEnd() }) } private fun handleUpdateFlag(cliArgs: Array, sourceLines: List): Array { diff --git a/tools/codegen/src/com/android/codegen/Printer.kt b/tools/codegen/src/com/android/codegen/Printer.kt new file mode 100644 index 000000000000..b30e3f68b307 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Printer.kt @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 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 com.android.codegen + +/** + * Mixin for syntactic sugar around indent-aware printing into [stringBuilder] + */ +interface Printer> { + + val stringBuilder: StringBuilder + + var currentIndent: String + + fun pushIndent() { + currentIndent += INDENT_SINGLE + } + + fun popIndent() { + currentIndent = if (currentIndent.length >= INDENT_SINGLE.length) { + currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length) + } else { + "" + } + } + + fun backspace() = stringBuilder.setLength(stringBuilder.length - 1) + val lastChar get() = stringBuilder[stringBuilder.length - 1] + + private fun appendRaw(s: String) { + stringBuilder.append(s) + } + + fun append(s: String) { + if (s.isBlank() && s != "\n") { + appendRaw(s) + } else { + appendRaw(s.lines().map { line -> + if (line.startsWith(" *")) line else line.trimStart() + }.joinToString("\n$currentIndent")) + } + } + + fun appendSameLine(s: String) { + while (lastChar.isWhitespace() || lastChar.isNewline()) { + backspace() + } + appendRaw(s) + } + + fun rmEmptyLine() { + while (lastChar.isWhitespaceNonNewline()) backspace() + if (lastChar.isNewline()) backspace() + } + + /** + * Syntactic sugar for: + * ``` + * +"code()"; + * ``` + * to append the given string plus a newline + */ + operator fun String.unaryPlus() = append("$this\n") + + /** + * Syntactic sugar for: + * ``` + * !"code()"; + * ``` + * to append the given string without a newline + */ + operator fun String.not() = append(this) + + /** + * Syntactic sugar for: + * ``` + * ... { + * ... + * }+";" + * ``` + * to append a ';' on same line after a block, and a newline afterwards + */ + operator fun Unit.plus(s: String) { + appendSameLine(s) + +"" + } + + /** + * A multi-purpose syntactic sugar for appending the given string plus anything generated in + * the given [block], the latter with the appropriate deeper indent, + * and resetting the indent back to original at the end + * + * Usage examples: + * + * ``` + * "if (...)" { + * ... + * } + * ``` + * to append a corresponding if block appropriate indentation + * + * ``` + * "void foo(...)" { + * ... + * } + * ``` + * similar to the previous one, plus an extra empty line after the function body + * + * ``` + * "void foo(" { + * + * } + * ``` + * to use proper indentation for args code and close the bracket on same line at end + * + * ``` + * "..." { + * ... + * } + * to use the correct indentation for inner code, resetting it at the end + */ + operator fun String.invoke(block: SELF.() -> Unit) { + if (this == " {") { + appendSameLine(this) + } else { + append(this) + } + when { + endsWith("(") -> { + indentedBy(2, block) + appendSameLine(")") + } + endsWith("{") || endsWith(")") -> { + if (!endsWith("{")) appendSameLine(" {") + indentedBy(1, block) + +"}" + if ((endsWith(") {") || endsWith(")") || this == " {") + && !startsWith("synchronized") + && !startsWith("switch") + && !startsWith("if ") + && !contains(" else ") + && !contains("new ") + && !contains("return ")) { + +"" // extra line after function definitions + } + } + else -> indentedBy(2, block) + } + } + + fun indentedBy(level: Int, block: SELF.() -> Unit) { + append("\n") + level times { + append(INDENT_SINGLE) + pushIndent() + } + (this as SELF).block() + level times { popIndent() } + rmEmptyLine() + +"" + } + + fun Iterable.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { + forEachApply { + b() + if (isLast) { + while (lastChar == ' ' || lastChar == '\n') backspace() + if (lastChar == ',') backspace() + } + } + } +} \ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index e703397214eb..c19ae3b0b11f 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -1,5 +1,11 @@ package com.android.codegen +import com.github.javaparser.JavaParser +import com.github.javaparser.ParseProblemException +import com.github.javaparser.ParseResult +import com.github.javaparser.ast.Node +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration +import com.github.javaparser.ast.body.TypeDeclaration import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.nodeTypes.NodeWithModifiers import java.time.Instant @@ -92,3 +98,49 @@ val AnnotationExpr.args: Map get() = when (this) { is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap() else -> throw IllegalArgumentException("Unknown annotation expression: $this") } + +val TypeDeclaration<*>.nestedTypes get() = childNodes.filterIsInstance>() +val TypeDeclaration<*>.nestedDataClasses get() + = nestedTypes.filterIsInstance() + .filter { it.annotations.any { it.nameAsString.endsWith("DataClass") } } +val TypeDeclaration<*>.startLine get() = range.get()!!.begin.line + +inline fun List.forEachSequentialPair(action: (T, T?) -> Unit) { + forEachIndexed { index, t -> + action(t, getOrNull(index + 1)) + } +} + +fun parseJava(fn: JavaParser.(String) -> ParseResult, source: String): T = try { + val parse = JAVA_PARSER.fn(source) + if (parse.problems.isNotEmpty()) { + throw parseFailed( + source, + desc = parse.problems.joinToString("\n"), + cause = parse.problems.mapNotNull { it.cause.orElse(null) }.firstOrNull()) + } + parse.result.get() +} catch (e: ParseProblemException) { + throw parseFailed(source, cause = e) +} + +private fun parseFailed(source: String, cause: Throwable? = null, desc: String = ""): RuntimeException { + return RuntimeException("Failed to parse code:\n" + + source + .lines() + .mapIndexed { lnNum, ln -> "/*$lnNum*/$ln" } + .joinToString("\n") + "\n$desc", + cause) +} + +var MutableList.last + get() = last() + set(value) { + if (isEmpty()) { + add(value) + } else { + this[size - 1] = value + } + } + +inline fun buildList(init: MutableList.() -> Unit) = mutableListOf().apply(init) \ No newline at end of file diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index 7fe21c7aab3e..51faa49a86cc 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -208,7 +208,16 @@ class StaleDataclassProcessor: AbstractProcessor() { val refreshCmd = if (file != null) { "$CODEGEN_NAME $file" } else { - "find \$ANDROID_BUILD_TOP -path */${clazz.replace('.', '/')}.java -exec $CODEGEN_NAME {} \\;" + var gotTopLevelCalssName = false + val filePath = clazz.split(".") + .takeWhile { word -> + if (!gotTopLevelCalssName && word[0].isUpperCase()) { + gotTopLevelCalssName = true + return@takeWhile true + } + !gotTopLevelCalssName + }.joinToString("/") + "find \$ANDROID_BUILD_TOP -path */$filePath.java -exec $CODEGEN_NAME {} \\;" } } -- cgit v1.2.3-59-g8ed1b From 7daee0bba47aa72a5d151b8218fbeaa03acbb50b Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Fri, 1 Nov 2019 17:51:27 -0700 Subject: [codegen] Fix generation for files without end markers This fixes a bug where cedegen fails for files that were generated before end of generated code section markers were introduced. Test: . master/frameworks/base/tests/Codegen/runTest.sh Change-Id: Ia68e8124fcbdeeb2052067ba8731e68e42007529 --- .../codegentest/HierrarchicalDataClassBase.java | 6 +++--- .../codegentest/HierrarchicalDataClassChild.java | 6 +++--- .../codegentest/ParcelAllTheThingsDataClass.java | 6 +++--- .../src/com/android/codegentest/SampleDataClass.java | 6 +++--- .../android/codegentest/SampleWithCustomBuilder.java | 6 +++--- .../codegentest/SampleWithNestedDataClasses.java | 18 +++++++++--------- .../StaleDataclassDetectorFalsePositivesTest.java | 6 +++--- tools/codegen/src/com/android/codegen/FileInfo.kt | 2 +- .../codegen/src/com/android/codegen/SharedConstants.kt | 2 +- 9 files changed, 29 insertions(+), 29 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 56ad217e6d19..7d88161a2805 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -98,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1572630437620L, - codegenVersion = "1.0.11", + time = 1572655992854L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 59e07c471d63..c930ce535ab6 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1572630438646L, - codegenVersion = "1.0.11", + time = 1572655993858L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 3c00a3a63eca..368a5c3a81e8 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -52,7 +52,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -410,8 +410,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1572630436563L, - codegenVersion = "1.0.11", + time = 1572655991821L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 9765bdc261db..9d52287d631e 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1872,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1572630434434L, - codegenVersion = "1.0.11", + time = 1572655989589L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index c62f2b8f6acb..cef32d1051df 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -253,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1572630435484L, - codegenVersion = "1.0.11", + time = 1572655990725L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java index 5b93b38161e3..27055f6a4df8 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java @@ -36,7 +36,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -135,8 +135,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572630440713L, - codegenVersion = "1.0.11", + time = 1572655995915L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -160,7 +160,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -259,8 +259,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572630440724L, - codegenVersion = "1.0.11", + time = 1572655995924L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -274,7 +274,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -373,8 +373,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572630440729L, - codegenVersion = "1.0.11", + time = 1572655995930L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 2964451225e4..4bfec895fdcb 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.11. + // Code below generated by codegen v1.0.12. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,8 +65,8 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1572630439617L, - codegenVersion = "1.0.11", + time = 1572655994865L, + codegenVersion = "1.0.12", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/FileInfo.kt b/tools/codegen/src/com/android/codegen/FileInfo.kt index 9c15fbf84223..909472640f29 100644 --- a/tools/codegen/src/com/android/codegen/FileInfo.kt +++ b/tools/codegen/src/com/android/codegen/FileInfo.kt @@ -164,7 +164,7 @@ class FileInfo( } if (endInclusive == -1) { // Legacy generated code doesn't have end markers - endInclusive = fileInfo.sourceLines.size - 2 + endInclusive = sourceNoPrefix.size - 2 } IntRange( range.start + start - fileInfo.generatedWarningNumPrecedingEmptyLines, diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 270d34a01a64..339057f24833 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.11" +const val CODEGEN_VERSION = "1.0.12" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From c5c636a5ea9942190f67eb81d83a553f97197e83 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 22 Oct 2019 10:37:26 -0700 Subject: Fix codegen ArrayMap fields Fixes: 143112074 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: Iec0fa89a91955f8b4e4b5b8d4dd9d7f0b30c608f --- .../codegentest/HierrarchicalDataClassBase.java | 6 +++--- .../codegentest/HierrarchicalDataClassChild.java | 6 +++--- .../codegentest/ParcelAllTheThingsDataClass.java | 8 +++++--- .../src/com/android/codegentest/SampleDataClass.java | 6 +++--- .../android/codegentest/SampleWithCustomBuilder.java | 6 +++--- .../codegentest/SampleWithNestedDataClasses.java | 18 +++++++++--------- .../StaleDataclassDetectorFalsePositivesTest.java | 6 +++--- tools/codegen/src/com/android/codegen/FieldInfo.kt | 4 +++- tools/codegen/src/com/android/codegen/Generators.kt | 7 ++++--- .../codegen/src/com/android/codegen/SharedConstants.kt | 2 +- 10 files changed, 37 insertions(+), 32 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 7d88161a2805..176c7a0ada7b 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -98,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1572655992854L, - codegenVersion = "1.0.12", + time = 1573006405823L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index c930ce535ab6..e348c77c0ac6 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1572655993858L, - codegenVersion = "1.0.12", + time = 1573006406833L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 368a5c3a81e8..112d3df02280 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArrayMap; import android.util.SparseArray; import android.util.SparseIntArray; @@ -52,7 +53,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.12. + + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -410,8 +412,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1572655991821L, - codegenVersion = "1.0.12", + time = 1573006404728L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 9d52287d631e..0fdcf5918c81 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1872,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1572655989589L, - codegenVersion = "1.0.12", + time = 1573006402566L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index cef32d1051df..cab477dafae9 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -253,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1572655990725L, - codegenVersion = "1.0.12", + time = 1573006403628L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java index 27055f6a4df8..6190085a4d82 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java @@ -36,7 +36,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -135,8 +135,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572655995915L, - codegenVersion = "1.0.12", + time = 1573006408903L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -160,7 +160,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -259,8 +259,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572655995924L, - codegenVersion = "1.0.12", + time = 1573006408912L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -274,7 +274,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -373,8 +373,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1572655995930L, - codegenVersion = "1.0.12", + time = 1573006408917L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 4bfec895fdcb..5a960d7dd002 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.12. + // Code below generated by codegen v1.0.13. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,8 +65,8 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1572655994865L, - codegenVersion = "1.0.12", + time = 1573006407900L, + codegenVersion = "1.0.13", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index ed35a1dfc599..ebfbbd8163b5 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -93,6 +93,8 @@ data class FieldInfo( // Generic args val isArray = Type.endsWith("[]") val isList = FieldClass == "List" || FieldClass == "ArrayList" + val isMap = FieldClass == "Map" || FieldClass == "ArrayMap" + || FieldClass == "HashMap" || FieldClass == "LinkedHashMap" val fieldBit = bitAtExpr(index) var isLast = false val isFinal = fieldAst.isFinal @@ -197,7 +199,7 @@ data class FieldInfo( listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle", "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") -> FieldClass - FieldClass == "Map" && fieldTypeGenegicArgs[0] == "String" -> "Map" + isMap && fieldTypeGenegicArgs[0] == "String" -> "Map" isArray -> when { FieldInnerType!! in (PRIMITIVE_TYPES + "String") -> FieldInnerType + "Array" isBinder(FieldInnerType) -> "BinderArray" diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index c25d0c74f251..dc1f4c50abc4 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -343,7 +343,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { } } - if (FieldClass.endsWith("Map") && FieldInnerType != null) { + if (isMap && FieldInnerType != null) { generateBuilderMethod( name = adderName, defVisibility = visibility, @@ -351,7 +351,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { paramNames = listOf("key", "value"), genJavadoc = { +javadocSeeSetter }) { !singularNameCustomizationHint - +"if ($name == null) $setterName(new $LinkedHashMap());" + +"if ($name == null) $setterName(new ${if (FieldClass == "Map") LinkedHashMap else FieldClass}());" +"$name.put(key, value);" +"return$maybeCast this;" } @@ -507,7 +507,8 @@ fun ClassPrinter.generateParcelable() { // Create container if any val containerInitExpr = when { - FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" + FieldClass == "Map" -> "new $LinkedHashMap<>()" + isMap -> "new $FieldClass()" FieldClass == "List" || FieldClass == "ArrayList" -> "new ${classRef("java.util.ArrayList")}<>()" else -> "" diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 339057f24833..85c832fcf34f 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.12" +const val CODEGEN_VERSION = "1.0.13" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From f745042916b5dbae69f44769722c094454b98228 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Mon, 18 Nov 2019 16:09:52 -0800 Subject: Add @NonNull annotation to Builder.addXXX args API guidelines is for each non-primitive arg to have a nullablility annotation. Since it's very rate for collections to permit null values, just assume collections only accept @NunNull emenents for now. Fixes: 144725154 Test: . frameworks/base/tests/Codegen/runTest.sh Change-Id: I333327490bd8eb9bb031f062c80283bd405173af --- .../codegentest/HierrarchicalDataClassBase.java | 6 +++--- .../codegentest/HierrarchicalDataClassChild.java | 6 +++--- .../codegentest/ParcelAllTheThingsDataClass.java | 12 ++++++------ .../src/com/android/codegentest/SampleDataClass.java | 10 +++++----- .../android/codegentest/SampleWithCustomBuilder.java | 6 +++--- .../codegentest/SampleWithNestedDataClasses.java | 18 +++++++++--------- .../StaleDataclassDetectorFalsePositivesTest.java | 6 +++--- tools/codegen/src/com/android/codegen/ClassPrinter.kt | 8 +++++++- tools/codegen/src/com/android/codegen/Generators.kt | 14 ++++++++++---- .../codegen/src/com/android/codegen/SharedConstants.kt | 2 +- 10 files changed, 50 insertions(+), 38 deletions(-) (limited to 'tools/codegen') diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 176c7a0ada7b..339df93bbca7 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -98,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1573006405823L, - codegenVersion = "1.0.13", + time = 1574122837821L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index e348c77c0ac6..69e06b21ce5e 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1573006406833L, - codegenVersion = "1.0.13", + time = 1574122838768L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 112d3df02280..ca128beb53d9 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -54,7 +54,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -291,7 +291,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { /** @see #setStringList */ @DataClass.Generated.Member - public @NonNull Builder addStringList(String value) { + public @NonNull Builder addStringList(@NonNull String value) { // You can refine this method's name by providing item's singular name, e.g.: // @DataClass.PluralOf("item")) mItems = ... @@ -310,7 +310,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { /** @see #setMap */ @DataClass.Generated.Member - public @NonNull Builder addMap(String key, SampleWithCustomBuilder value) { + public @NonNull Builder addMap(@NonNull String key, @NonNull SampleWithCustomBuilder value) { // You can refine this method's name by providing item's singular name, e.g.: // @DataClass.PluralOf("item")) mItems = ... @@ -329,7 +329,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { /** @see #setStringMap */ @DataClass.Generated.Member - public @NonNull Builder addStringMap(String key, String value) { + public @NonNull Builder addStringMap(@NonNull String key, @NonNull String value) { // You can refine this method's name by providing item's singular name, e.g.: // @DataClass.PluralOf("item")) mItems = ... @@ -412,8 +412,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1573006404728L, - codegenVersion = "1.0.13", + time = 1574122836960L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 0fdcf5918c81..c850bf8002c0 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -1636,7 +1636,7 @@ public final class SampleDataClass implements Parcelable { /** @see #setLinkAddresses2 */ @DataClass.Generated.Member - public @NonNull Builder addLinkAddresses2(LinkAddress value) { + public @NonNull Builder addLinkAddresses2(@NonNull LinkAddress value) { // You can refine this method's name by providing item's singular name, e.g.: // @DataClass.PluralOf("item")) mItems = ... @@ -1661,7 +1661,7 @@ public final class SampleDataClass implements Parcelable { /** @see #setLinkAddresses */ @DataClass.Generated.Member - public @NonNull Builder addLinkAddress(LinkAddress value) { + public @NonNull Builder addLinkAddress(@NonNull LinkAddress value) { if (mLinkAddresses == null) setLinkAddresses(new ArrayList<>()); mLinkAddresses.add(value); return this; @@ -1872,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1573006402566L, - codegenVersion = "1.0.13", + time = 1574122835009L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index cab477dafae9..2de848c83eda 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -253,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1573006403628L, - codegenVersion = "1.0.13", + time = 1574122835982L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java index 6190085a4d82..0deffe44838f 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java @@ -36,7 +36,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -135,8 +135,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1573006408903L, - codegenVersion = "1.0.13", + time = 1574122840588L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -160,7 +160,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -259,8 +259,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1573006408912L, - codegenVersion = "1.0.13", + time = 1574122840597L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -274,7 +274,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -373,8 +373,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1573006408917L, - codegenVersion = "1.0.13", + time = 1574122840608L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 5a960d7dd002..712722b123af 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.13. + // Code below generated by codegen v1.0.14. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,8 +65,8 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1573006407900L, - codegenVersion = "1.0.13", + time = 1574122839646L, + codegenVersion = "1.0.14", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index a4fd374d0c6e..c7c80bab67bf 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -164,7 +164,13 @@ class ClassPrinter( getSuppressedMembers(classAst) } val builderSuppressedMembers by lazy { - getSuppressedMembers(customBaseBuilderAst) + getSuppressedMembers(customBaseBuilderAst) + suppressedMembers.mapNotNull { + if (it.startsWith("$CANONICAL_BUILDER_CLASS.")) { + it.removePrefix("$CANONICAL_BUILDER_CLASS.") + } else { + null + } + } } private fun getSuppressedMembers(clazz: ClassOrInterfaceDeclaration?): List { diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index dc1f4c50abc4..8fe243ff68cb 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -270,7 +270,7 @@ fun ClassPrinter.generateBuilder() { private fun ClassPrinter.generateBuilderMethod( defVisibility: String, name: String, - ParamAnnotations: String? = null, + paramAnnotations: String? = null, paramTypes: List, paramNames: List = listOf("value"), genJavadoc: ClassPrinter.() -> Unit, @@ -292,8 +292,12 @@ private fun ClassPrinter.generateBuilderMethod( +GENERATED_MEMBER_HEADER if (providedMethod?.isAbstract == true) +"@Override" if (!Annotations.isNullOrEmpty()) +Annotations - "$visibility @$NonNull $ReturnType $name(${if_(!ParamAnnotations.isNullOrEmpty(), "$ParamAnnotations ")}${ - paramTypes.zip(paramNames).joinToString(", ") { (Type, paramName) -> "$Type $paramName" } + val ParamAnnotations = if (!paramAnnotations.isNullOrEmpty()) "$paramAnnotations " else "" + + "$visibility @$NonNull $ReturnType $name(${ + paramTypes.zip(paramNames).joinToString(", ") { (Type, paramName) -> + "$ParamAnnotations$Type $paramName" + } })" { genBody() } @@ -311,7 +315,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { generateBuilderMethod( name = setterName, defVisibility = visibility, - ParamAnnotations = annotationsNoInternal.joinToString(" "), + paramAnnotations = annotationsNoInternal.joinToString(" "), paramTypes = listOf(SetterParamType), genJavadoc = { generateFieldJavadoc() }) { +"checkNotUsed();" @@ -333,6 +337,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { generateBuilderMethod( name = adderName, defVisibility = visibility, + paramAnnotations = "@$NonNull", paramTypes = listOf(FieldInnerType), genJavadoc = { +javadocSeeSetter }) { @@ -347,6 +352,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { generateBuilderMethod( name = adderName, defVisibility = visibility, + paramAnnotations = "@$NonNull", paramTypes = fieldTypeGenegicArgs, paramNames = listOf("key", "value"), genJavadoc = { +javadocSeeSetter }) { diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 85c832fcf34f..74c86f4551f8 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.13" +const val CODEGEN_VERSION = "1.0.14" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b From 192ca30b7a8b8a1220db8bf9966e7d807f856f65 Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Wed, 22 Jan 2020 21:38:23 +0000 Subject: Remove unused UnsupportedAppUsage annotations. The new annotation is android.compat.annotation.UnsupportedAppUsage. Test: m Bug: 145132366 Exempt-From-Owner-Approval: previously approved change Change-Id: Ie12e28eee0ed20b5677ee3162143700813b7ab64 --- Android.bp | 3 - .../android/annotation/UnsupportedAppUsage.java | 170 --------------------- .../inputmethod/InputMethodManagerService.java | 2 +- .../src/com/android/codegen/ImportsProvider.kt | 2 +- 4 files changed, 2 insertions(+), 175 deletions(-) delete mode 100644 core/java/android/annotation/UnsupportedAppUsage.java (limited to 'tools/codegen') diff --git a/Android.bp b/Android.bp index df276a053dfc..ffb4d3a6ff73 100644 --- a/Android.bp +++ b/Android.bp @@ -583,7 +583,6 @@ java_library { host_supported: true, srcs: [ "core/java/android/annotation/IntDef.java", - "core/java/android/annotation/UnsupportedAppUsage.java", ], static_libs: [ "art.module.api.annotations", @@ -664,7 +663,6 @@ filegroup { "core/java/android/annotation/SystemApi.java", "core/java/android/annotation/SystemService.java", "core/java/android/annotation/TestApi.java", - "core/java/android/annotation/UnsupportedAppUsage.java", "core/java/com/android/internal/annotations/GuardedBy.java", "core/java/com/android/internal/annotations/VisibleForTesting.java", "core/java/com/android/internal/annotations/Immutable.java", @@ -1073,7 +1071,6 @@ java_library { "core/java/android/annotation/Nullable.java", "core/java/android/annotation/SystemApi.java", "core/java/android/annotation/TestApi.java", - "core/java/android/annotation/UnsupportedAppUsage.java", "core/java/android/os/HidlMemory.java", "core/java/android/os/HwBinder.java", "core/java/android/os/HwBlob.java", diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java deleted file mode 100644 index 1af48cb63079..000000000000 --- a/core/java/android/annotation/UnsupportedAppUsage.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2018 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.annotation; - -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.CLASS; - -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Indicates that this non-SDK interface is used by apps. A non-SDK interface is a - * class member (field or method) that is not part of the public SDK. Since the - * member is not part of the SDK, usage by apps is not supported. - * - *

If you are an Android App developer

- * - * This annotation indicates that you may be able to access the member, but that - * this access is discouraged and not supported by Android. If there is a value - * for {@link #maxTargetSdk()} on the annotation, access will be restricted based - * on the {@code targetSdkVersion} value set in your manifest. - * - *

Fields and methods annotated with this are likely to be restricted, changed - * or removed in future Android releases. If you rely on these members for - * functionality that is not otherwise supported by Android, consider filing a - * feature request. - * - *

If you are an Android OS developer

- * - * This annotation acts as a heads up that changing a given method or field - * may affect apps, potentially breaking them when the next Android version is - * released. In some cases, for members that are heavily used, this annotation - * may imply restrictions on changes to the member. - * - *

This annotation also results in access to the member being permitted by the - * runtime, with a warning being generated in debug builds. Which apps can access - * the member is determined by the value of {@link #maxTargetSdk()}. - * - *

For more details, see go/UnsupportedAppUsage. - * - * {@hide} - */ -@Retention(CLASS) -@Target({CONSTRUCTOR, METHOD, FIELD, TYPE}) -@Repeatable(UnsupportedAppUsage.Container.class) -public @interface UnsupportedAppUsage { - - /** - * Associates a bug tracking the work to add a public alternative to this API. Optional. - * - * @return ID of the associated tracking bug - */ - long trackingBug() default 0; - - /** - * Indicates that usage of this API is limited to apps based on their target SDK version. - * - *

Access to the API is allowed if the targetSdkVersion in the apps manifest is no greater - * than this value. Access checks are performed at runtime. - * - *

This is used to give app developers a grace period to migrate off a non-SDK interface. - * When making Android version N, existing APIs can have a maxTargetSdk of N-1 added to them. - * Developers must then migrate off the API when their app is updated in future, but it will - * continue working in the meantime. - * - *

Possible values are: - *

    - *
  • - * An API level like {@link android.os.Build.VERSION_CODES#O} - in which case the API is - * available up to and including the specified release. Or, in other words, the API is - * blacklisted (unavailable) from the next API level from the one specified. - *
  • - *
  • - * absent (default value) - All apps can access this API, but doing so may result in - * warnings in the log, UI warnings (on developer builds) and/or strictmode violations. - * The API is likely to be further restricted in future. - *
  • - * - *
- * - * @return The maximum value for an apps targetSdkVersion in order to access this API. - */ - int maxTargetSdk() default Integer.MAX_VALUE; - - /** - * For debug use only. The expected dex signature to be generated for this API, used to verify - * parts of the build process. - * - * @return A dex API signature. - */ - String expectedSignature() default ""; - - /** - * The signature of an implicit (not present in the source) member that forms part of the - * hiddenapi. - * - *

Allows access to non-SDK API elements that are not represented in the input source to be - * managed. - * - *

This must only be used when applying the annotation to a type, using it in any other - * situation is an error. - * - * @return A dex API signature. - */ - String implicitMember() default ""; - - /** - * Public API alternatives to this API. - * - *

If non-empty, the string must be a description of the public API alternative(s) to this - * API. The explanation must contain at least one Javadoc link tag to public API methods or - * fields. e.g.: - * {@literal @UnsupportedAppUsage(publicAlternatives="Use {@link foo.bar.Baz#bat()} instead.")} - * - *

Any elements that can be deduced can be omitted, e.g.: - *

    - *
  • - * the class, if it's the same as for the annotated element. - *
  • - *
  • - * the package name, if it's the same as for the annotated element. - *
  • - *
  • - * the method parameters, if there is only one method with that name in the given - * package and class. - *
  • - *
- * @return A Javadoc-formatted string. - */ - @SuppressWarnings("JavadocReference") - String publicAlternatives() default ""; - - /** - * Override the default source position when generating an index of the annotations. - * - *

This is intended for use by tools that generate java source code, to point to the - * original source position of the annotation, rather than the position within the generated - * code. It should never be set manually. - * - *

The format of the value is "path/to/file:startline:startcol:endline:endcol" indicating - * the position of the annotation itself. - */ - String overrideSourcePosition() default ""; - - /** - * Container for {@link UnsupportedAppUsage} that allows it to be applied repeatedly to types. - */ - @Retention(CLASS) - @Target(TYPE) - @interface Container { - UnsupportedAppUsage[] value(); - } -} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 905a94f57262..15698e9795ec 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3510,7 +3510,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** - * This is kept due to {@link android.annotation.UnsupportedAppUsage} in + * This is kept due to {@code @UnsupportedAppUsage} in * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in * {@link InputMethodService#onCreate()}. * diff --git a/tools/codegen/src/com/android/codegen/ImportsProvider.kt b/tools/codegen/src/com/android/codegen/ImportsProvider.kt index ba0a0318c843..c830aaa0df3d 100644 --- a/tools/codegen/src/com/android/codegen/ImportsProvider.kt +++ b/tools/codegen/src/com/android/codegen/ImportsProvider.kt @@ -46,7 +46,7 @@ interface ImportsProvider { val Parcelling: String get() { return classRef("com.android.internal.util.Parcelling") } val Parcelable: String get() { return classRef("android.os.Parcelable") } val Parcel: String get() { return classRef("android.os.Parcel") } - val UnsupportedAppUsage: String get() { return classRef("android.annotation.UnsupportedAppUsage") } + val UnsupportedAppUsage: String get() { return classRef("android.compat.annotation.UnsupportedAppUsage") } /** * Optionally shortens a class reference if there's a corresponding import present -- cgit v1.2.3-59-g8ed1b From 47ca5d035e039205b68b310b66d3d1b04318966a Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 25 Feb 2020 18:19:50 -0800 Subject: Generate @NonNull setter args and @MaySetToNull to opt out Bug: 143653080 Test: source /usr/local/google/home/eugenesusla/android2/master/frameworks/base/tests/Codegen/runTest.sh Change-Id: Iba8da34c91fd09e266429ab131add1d53449506b (cherry picked from commit ab14c55c1f085153f09741e497eb0cdeee38f0e1) --- core/java/com/android/internal/util/DataClass.java | 15 ++++++++- .../codegentest/HierrarchicalDataClassBase.java | 8 ++--- .../codegentest/HierrarchicalDataClassChild.java | 6 ++-- .../codegentest/ParcelAllTheThingsDataClass.java | 8 ++--- .../com/android/codegentest/SampleDataClass.java | 36 +++++++++++----------- .../codegentest/SampleWithCustomBuilder.java | 6 ++-- .../codegentest/SampleWithNestedDataClasses.java | 18 +++++------ .../StaleDataclassDetectorFalsePositivesTest.java | 6 ++-- .../src/com/android/codegen/ClassPrinter.kt | 2 +- tools/codegen/src/com/android/codegen/FieldInfo.kt | 14 +++++++-- .../codegen/src/com/android/codegen/Generators.kt | 2 +- .../src/com/android/codegen/ImportsProvider.kt | 1 + .../src/com/android/codegen/SharedConstants.kt | 2 +- 13 files changed, 74 insertions(+), 50 deletions(-) (limited to 'tools/codegen') diff --git a/core/java/com/android/internal/util/DataClass.java b/core/java/com/android/internal/util/DataClass.java index 43539c7c4011..ee139d90888f 100644 --- a/core/java/com/android/internal/util/DataClass.java +++ b/core/java/com/android/internal/util/DataClass.java @@ -15,7 +15,13 @@ */ package com.android.internal.util; -import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; import android.annotation.IntDef; import android.annotation.Nullable; @@ -242,6 +248,13 @@ public @interface DataClass { String[] value(); } + /** + * Mark that the field should have a {@link Nullable} argument for its setter. + */ + @Retention(RetentionPolicy.SOURCE) + @Target({FIELD}) + @interface MaySetToNull {} + /** * Callback used by {@link #genForEachField}. * diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 339df93bbca7..475305eb83a2 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,7 +32,7 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -51,7 +51,7 @@ public class HierrarchicalDataClassBase implements Parcelable { } @DataClass.Generated.Member - public HierrarchicalDataClassBase setBaseData(int value) { + public HierrarchicalDataClassBase setBaseData( int value) { mBaseData = value; return this; } @@ -98,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1574122837821L, - codegenVersion = "1.0.14", + time = 1582685650576L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 69e06b21ce5e..150b324d1a30 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,7 +46,7 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1574122838768L, - codegenVersion = "1.0.14", + time = 1582685651560L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index ca128beb53d9..30871566c269 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -54,7 +54,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -355,7 +355,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated.Member - public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) { + public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @NonNull Boolean value) { checkNotUsed(); mBuilderFieldsSet |= 0x80; mNullableBoolean = value; @@ -412,8 +412,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1574122836960L, - codegenVersion = "1.0.14", + time = 1582685649678L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List mStringList\n @android.annotation.NonNull java.util.Map mMap\n @android.annotation.NonNull java.util.Map mStringMap\n @android.annotation.NonNull android.util.SparseArray mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index c850bf8002c0..8d421bfa492a 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,7 +342,7 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -512,7 +512,7 @@ public final class SampleDataClass implements Parcelable { @Nullable LinkAddress[] linkAddresses5, @StringRes int stringRes, @android.annotation.IntRange(from = 0, to = 6) int dayOfWeek, - @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] coords) { + @Size(2) @NonNull @FloatRange(from = 0f) float[] coords) { this.mNum = num; this.mNum2 = num2; this.mNum4 = num4; @@ -790,7 +790,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] getCoords() { + public @Size(2) @NonNull @FloatRange(from = 0f) float[] getCoords() { return mCoords; } @@ -820,7 +820,7 @@ public final class SampleDataClass implements Parcelable { * pieces in multiple places for each field. */ @DataClass.Generated.Member - public SampleDataClass setNum(int value) { + public SampleDataClass setNum( int value) { mNum = value; return this; } @@ -832,7 +832,7 @@ public final class SampleDataClass implements Parcelable { * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. */ @DataClass.Generated.Member - public SampleDataClass setNum2(int value) { + public SampleDataClass setNum2( int value) { mNum2 = value; return this; } @@ -846,7 +846,7 @@ public final class SampleDataClass implements Parcelable { * @hide */ @DataClass.Generated.Member - public SampleDataClass setNum4(int value) { + public SampleDataClass setNum4( int value) { mNum4 = value; return this; } @@ -855,7 +855,7 @@ public final class SampleDataClass implements Parcelable { * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. */ @DataClass.Generated.Member - public SampleDataClass setName(@Nullable String value) { + public SampleDataClass setName(@NonNull String value) { mName = value; return this; } @@ -892,7 +892,7 @@ public final class SampleDataClass implements Parcelable { * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. */ @DataClass.Generated.Member - public SampleDataClass setOtherParcelable(@Nullable AccessibilityNodeInfo value) { + public SampleDataClass setOtherParcelable(@NonNull AccessibilityNodeInfo value) { mOtherParcelable = value; return this; } @@ -957,7 +957,7 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setLinkAddresses4(LinkAddress...) */ @DataClass.Generated.Member - public SampleDataClass setLinkAddresses4(@Nullable LinkAddress... value) { + public SampleDataClass setLinkAddresses4(@NonNull LinkAddress... value) { mLinkAddresses4 = value; return this; } @@ -1070,7 +1070,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public SampleDataClass setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { + public SampleDataClass setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) { mCoords = value; AnnotationValidations.validate( Size.class, null, mCoords.length, @@ -1451,7 +1451,7 @@ public final class SampleDataClass implements Parcelable { private @Nullable LinkAddress[] mLinkAddresses5; private @StringRes int mStringRes; private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek; - private @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] mCoords; + private @Size(2) @NonNull @FloatRange(from = 0f) float[] mCoords; private long mBuilderFieldsSet = 0L; @@ -1549,7 +1549,7 @@ public final class SampleDataClass implements Parcelable { * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields. */ @DataClass.Generated.Member - public @NonNull Builder setName(@Nullable String value) { + public @NonNull Builder setName(@NonNull String value) { checkNotUsed(); mBuilderFieldsSet |= 0x8; mName = value; @@ -1588,7 +1588,7 @@ public final class SampleDataClass implements Parcelable { * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. */ @DataClass.Generated.Member - public @NonNull Builder setOtherParcelable(@Nullable AccessibilityNodeInfo value) { + public @NonNull Builder setOtherParcelable(@NonNull AccessibilityNodeInfo value) { checkNotUsed(); mBuilderFieldsSet |= 0x40; mOtherParcelable = value; @@ -1674,7 +1674,7 @@ public final class SampleDataClass implements Parcelable { * @see Builder#setLinkAddresses4(LinkAddress...) */ @DataClass.Generated.Member - public @NonNull Builder setLinkAddresses4(@Nullable LinkAddress... value) { + public @NonNull Builder setLinkAddresses4(@NonNull LinkAddress... value) { checkNotUsed(); mBuilderFieldsSet |= 0x800; mLinkAddresses4 = value; @@ -1733,7 +1733,7 @@ public final class SampleDataClass implements Parcelable { * Final fields suppress generating a setter (when setters are requested). */ @DataClass.Generated.Member - public @NonNull Builder setLinkAddresses5(@Nullable LinkAddress... value) { + public @NonNull Builder setLinkAddresses5(@NonNull LinkAddress... value) { checkNotUsed(); mBuilderFieldsSet |= 0x10000; mLinkAddresses5 = value; @@ -1785,7 +1785,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @NonNull Builder setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { + public @NonNull Builder setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) { checkNotUsed(); mBuilderFieldsSet |= 0x80000; mCoords = value; @@ -1872,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1574122835009L, - codegenVersion = "1.0.14", + time = 1582685647656L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index 2de848c83eda..d9fe1fd5a465 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,7 +85,7 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -253,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1574122835982L, - codegenVersion = "1.0.14", + time = 1582685648622L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java index 0deffe44838f..f98d7b05c2d3 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java @@ -36,7 +36,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -135,8 +135,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1574122840588L, - codegenVersion = "1.0.14", + time = 1582685653406L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -160,7 +160,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -259,8 +259,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1574122840597L, - codegenVersion = "1.0.14", + time = 1582685653415L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated @@ -274,7 +274,7 @@ public class SampleWithNestedDataClasses { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -373,8 +373,8 @@ public class SampleWithNestedDataClasses { }; @DataClass.Generated( - time = 1574122840608L, - codegenVersion = "1.0.14", + time = 1582685653420L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java", inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 712722b123af..6b4fc2fa157f 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,7 +51,7 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.14. + // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -65,8 +65,8 @@ public class StaleDataclassDetectorFalsePositivesTest { @DataClass.Generated( - time = 1574122839646L, - codegenVersion = "1.0.14", + time = 1582685652436L, + codegenVersion = "1.0.15", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index c7c80bab67bf..b90e1bb3e7e7 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -100,7 +100,7 @@ class ClassPrinter( ?: emptyMap() val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, UnsupportedAppUsage, - DataClassSuppressConstDefs) + DataClassSuppressConstDefs, MaySetToNull, Each, DataClass) val knownNonValidationAnnotations = internalAnnotations + Each + Nullable /** diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index ebfbbd8163b5..02ebaef90f0b 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -147,9 +147,19 @@ data class FieldInfo( val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } } val SetterParamType = if (isArray) "$FieldInnerType..." else Type - val annotatedTypeForSetterParam by lazy { - (annotationsNoInternal + SetterParamType).joinToString(" ") + val annotationsForSetterParam by lazy { + buildList { + addAll(annotationsNoInternal) + classPrinter { + if ("@$Nullable" in annotations + && "@$MaySetToNull" !in annotations) { + remove("@$Nullable") + add("@$NonNull") + } + } + }.joinToString(" ") } + val annotatedTypeForSetterParam by lazy { "$annotationsForSetterParam $SetterParamType" } // Utilities diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 8fe243ff68cb..5a96cf1d9bdb 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -315,7 +315,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { generateBuilderMethod( name = setterName, defVisibility = visibility, - paramAnnotations = annotationsNoInternal.joinToString(" "), + paramAnnotations = annotationsForSetterParam, paramTypes = listOf(SetterParamType), genJavadoc = { generateFieldJavadoc() }) { +"checkNotUsed();" diff --git a/tools/codegen/src/com/android/codegen/ImportsProvider.kt b/tools/codegen/src/com/android/codegen/ImportsProvider.kt index c830aaa0df3d..27dd9587db25 100644 --- a/tools/codegen/src/com/android/codegen/ImportsProvider.kt +++ b/tools/codegen/src/com/android/codegen/ImportsProvider.kt @@ -39,6 +39,7 @@ interface ImportsProvider { val ParcelWith: String get() { return classRef("com.android.internal.util.DataClass.ParcelWith") } val PluralOf: String get() { return classRef("com.android.internal.util.DataClass.PluralOf") } val Each: String get() { return classRef("com.android.internal.util.DataClass.Each") } + val MaySetToNull: String get() { return classRef("com.android.internal.util.DataClass.MaySetToNull") } val DataClassGenerated: String get() { return classRef("com.android.internal.util.DataClass.Generated") } val DataClassSuppressConstDefs: String get() { return classRef("com.android.internal.util.DataClass.SuppressConstDefsGeneration") } val DataClassSuppress: String get() { return classRef("com.android.internal.util.DataClass.Suppress") } diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 74c86f4551f8..6f740cd663e3 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.14" +const val CODEGEN_VERSION = "1.0.15" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" -- cgit v1.2.3-59-g8ed1b