diff options
author | Eugene Susla <eugenesusla@google.com> | 2019-10-22 17:32:08 -0700 |
---|---|---|
committer | Eugene Susla <eugenesusla@google.com> | 2019-11-01 17:53:56 +0000 |
commit | 322e8b17721a6956e00407e8d431ceecbd245b5c (patch) | |
tree | 9fe5a43411281cf93c377a2d6fcd326a299ec97e /tools/codegen/src/com/android/codegen/ClassPrinter.kt | |
parent | 4a0b1750ef697bb4f329a3beffe88bf46e1e6385 (diff) |
[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
Diffstat (limited to 'tools/codegen/src/com/android/codegen/ClassPrinter.kt')
-rw-r--r-- | tools/codegen/src/com/android/codegen/ClassPrinter.kt | 282 |
1 files changed, 57 insertions, 225 deletions
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<String>, - private val stringBuilder: StringBuilder, - var cliArgs: Array<String> -) : ClassInfo(source) { + classAst: ClassOrInterfaceDeclaration, + fileInfo: FileInfo +) : ClassInfo(classAst, fileInfo), Printer<ClassPrinter>, 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 <reified T : Any> 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(" { - * <args code> - * } - * ``` - * 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<FieldInfo>.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { - forEachApply { - b() - if (isLast) { - while (lastChar == ' ' || lastChar == '\n') backspace() - if (lastChar == ',') backspace() - } - } - } inline operator fun <R> 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 |