1 /*
<lambda>null2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.room.compiler.codegen.java
18 
19 import androidx.room.compiler.codegen.JCodeBlock
20 import androidx.room.compiler.codegen.JCodeBlockBuilder
21 import androidx.room.compiler.codegen.XAnnotationSpec
22 import androidx.room.compiler.codegen.XCodeBlock
23 import androidx.room.compiler.codegen.XFunSpec
24 import androidx.room.compiler.codegen.XMemberName
25 import androidx.room.compiler.codegen.XName
26 import androidx.room.compiler.codegen.XPropertySpec
27 import androidx.room.compiler.codegen.XSpec
28 import androidx.room.compiler.codegen.XTypeName
29 import androidx.room.compiler.codegen.XTypeSpec
30 import androidx.room.compiler.codegen.impl.XAnnotationSpecImpl
31 import androidx.room.compiler.codegen.impl.XCodeBlockImpl
32 import androidx.room.compiler.codegen.impl.XFunSpecImpl
33 import androidx.room.compiler.codegen.impl.XPropertySpecImpl
34 import androidx.room.compiler.codegen.impl.XTypeSpecImpl
35 
36 internal class JavaCodeBlock(internal val actual: JCodeBlock) : XSpec(), XCodeBlock {
37 
38     internal class Builder(internal val actual: JCodeBlockBuilder) :
39         XSpec.Builder(), XCodeBlock.Builder {
40 
41         override fun add(code: XCodeBlock) = apply {
42             require(code is XCodeBlockImpl)
43             actual.add(code.java.actual)
44         }
45 
46         override fun add(format: String, vararg args: Any?) = apply {
47             actual.add(formatString(format), *formatArgs(args))
48         }
49 
50         override fun addStatement(format: String, vararg args: Any?) = apply {
51             actual.addStatement(formatString(format), *formatArgs(args))
52         }
53 
54         override fun addLocalVariable(
55             name: String,
56             typeName: XTypeName,
57             isMutable: Boolean,
58             assignExpr: XCodeBlock?
59         ) = apply {
60             val finalKeyword = if (isMutable) "" else "final "
61             if (assignExpr != null) {
62                 addStatement("$finalKeyword%T %L = %L", typeName, name, assignExpr)
63             } else {
64                 addStatement("$finalKeyword%T %L", typeName, name)
65             }
66         }
67 
68         override fun beginControlFlow(controlFlow: String, vararg args: Any?) = apply {
69             actual.beginControlFlow(formatString(controlFlow), *formatArgs(args))
70         }
71 
72         override fun nextControlFlow(controlFlow: String, vararg args: Any?) = apply {
73             actual.nextControlFlow(formatString(controlFlow), *formatArgs(args))
74         }
75 
76         override fun endControlFlow() = apply { actual.endControlFlow() }
77 
78         override fun indent() = apply { actual.indent() }
79 
80         override fun unindent() = apply { actual.unindent() }
81 
82         override fun build() = JavaCodeBlock(actual.build())
83 
84         // Converts '%' place holders to '$' for JavaPoet
85         private fun formatString(format: String): String {
86             // Replace KPoet's member name placeholder for a JPoet literal for a XMemberName arg.
87             return format
88                 .replace("%M", "\$L")
89                 // TODO(b/247241415): Very simple replace for now, but this will not work when
90                 //  emitting modulo expressions!
91                 .replace('%', '$')
92         }
93 
94         // Unwraps room.compiler.codegen types to their JavaPoet actual
95         // TODO(b/247242375): Consider improving by wrapping args.
96         private fun formatArgs(args: Array<out Any?>): Array<Any?> {
97             return Array(args.size) { index ->
98                 when (val arg = args[index]) {
99                     is XTypeName -> arg.java
100                     is XMemberName -> arg.java
101                     is XName -> arg.java
102                     is XTypeSpec -> (arg as XTypeSpecImpl).java.actual
103                     is XPropertySpec -> (arg as XPropertySpecImpl).java.actual
104                     is XFunSpec -> (arg as XFunSpecImpl).java.actual
105                     is XCodeBlock -> (arg as XCodeBlockImpl).java.actual
106                     is XAnnotationSpec -> (arg as XAnnotationSpecImpl).java.actual
107                     is XTypeSpec.Builder,
108                     is XPropertySpec.Builder,
109                     is XFunSpec.Builder,
110                     is XCodeBlock.Builder,
111                     is XAnnotationSpec.Builder ->
112                         error("Found builder, ${arg.javaClass}. Did you forget to call .build()?")
113                     else -> arg
114                 }
115             }
116         }
117     }
118 }
119