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
186 lines
4.9 KiB
Kotlin
186 lines
4.9 KiB
Kotlin
/*
|
|
* 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<SELF: Printer<SELF>> {
|
|
|
|
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(" {
|
|
* <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
|
|
*/
|
|
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<FieldInfo>.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) {
|
|
forEachApply {
|
|
b()
|
|
if (isLast) {
|
|
while (lastChar == ' ' || lastChar == '\n') backspace()
|
|
if (lastChar == ',') backspace()
|
|
}
|
|
}
|
|
}
|
|
} |