• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 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 com.android.tools.metalava.model.turbine
18 
19 import com.android.tools.metalava.model.ANNOTATION_ATTR_VALUE
20 import com.android.tools.metalava.model.AnnotationAttribute
21 import com.android.tools.metalava.model.AnnotationAttributeValue
22 import com.android.tools.metalava.model.AnnotationItem
23 import com.android.tools.metalava.model.Codebase
24 import com.android.tools.metalava.model.DefaultAnnotationArrayAttributeValue
25 import com.android.tools.metalava.model.DefaultAnnotationAttribute
26 import com.android.tools.metalava.model.DefaultAnnotationItem
27 import com.android.tools.metalava.model.DefaultAnnotationSingleAttributeValue
28 import com.android.tools.metalava.reporter.FileLocation
29 import com.google.common.collect.ImmutableList
30 import com.google.common.collect.ImmutableMap
31 import com.google.turbine.model.Const
32 import com.google.turbine.model.Const.ArrayInitValue
33 import com.google.turbine.model.Const.Kind
34 import com.google.turbine.tree.Tree
35 import com.google.turbine.tree.Tree.ArrayInit
36 import com.google.turbine.tree.Tree.Assign
37 import com.google.turbine.tree.Tree.Expression
38 import com.google.turbine.tree.Tree.Literal
39 import com.google.turbine.type.AnnoInfo
40 
41 /**
42  * Factory for creating [AnnotationItem]s from [AnnoInfo]s.
43  *
44  * @param codebase the [Codebase] to which the [AnnotationItem] will belong.
45  * @param sourceFileCache provides mapping from [AnnoInfo.source] to location.
46  */
47 internal class TurbineAnnotationFactory(
48     private val codebase: Codebase,
49     private val sourceFileCache: TurbineSourceFileCache,
50 ) {
51     /** Creates a list of AnnotationItems from given list of Turbine Annotations */
52     internal fun createAnnotations(annotations: List<AnnoInfo>): List<AnnotationItem> {
53         return annotations.mapNotNull { createAnnotation(it) }
54     }
55 
56     /** Create an [AnnotationItem] from an [AnnoInfo]. */
57     private fun createAnnotation(annotation: AnnoInfo): AnnotationItem? {
58         // Get the source representation of the annotation. This will be null for an annotation
59         // loaded from a class file.
60         val tree: Tree.Anno? = annotation.tree()
61         // An annotation that has no definition in scope has a null sym, in that case fall back
62         // to use the name used in the source. The sym can only be null in sources, so if sym is
63         // null then tree cannot be null.
64         val qualifiedName = annotation.sym()?.qualifiedName ?: tree!!.name().dotSeparatedName
65 
66         val fileLocation =
67             annotation
68                 .source()
69                 ?.let { sourceFile -> sourceFileCache.turbineSourceFile(sourceFile) }
70                 ?.let { sourceFile -> TurbineFileLocation.forTree(sourceFile, tree) }
71                 ?: FileLocation.UNKNOWN
72 
73         return DefaultAnnotationItem.create(codebase, fileLocation, qualifiedName) {
74             getAnnotationAttributes(annotation.values(), tree?.args())
75         }
76     }
77 
78     /** Creates a list of AnnotationAttribute from the map of name-value attribute pairs */
79     private fun getAnnotationAttributes(
80         attrs: ImmutableMap<String, Const>,
81         exprs: ImmutableList<Expression>?
82     ): List<AnnotationAttribute> {
83         val attributes = mutableListOf<AnnotationAttribute>()
84         if (exprs != null) {
85             for (exp in exprs) {
86                 when (exp.kind()) {
87                     Tree.Kind.ASSIGN -> {
88                         exp as Assign
89                         val name = exp.name().value()
90                         val assignExp = exp.expr()
91                         attributes.add(
92                             DefaultAnnotationAttribute(
93                                 name,
94                                 createAttrValue(attrs[name]!!, assignExp)
95                             )
96                         )
97                     }
98                     else -> {
99                         val name = ANNOTATION_ATTR_VALUE
100                         val value =
101                             attrs[name]
102                                 ?: (exp as? Literal)?.value()
103                                     ?: error(
104                                     "Cannot find value for default 'value' attribute from $exp"
105                                 )
106                         attributes.add(
107                             DefaultAnnotationAttribute(name, createAttrValue(value, exp))
108                         )
109                     }
110                 }
111             }
112         } else {
113             for ((name, value) in attrs) {
114                 attributes.add(DefaultAnnotationAttribute(name, createAttrValue(value, null)))
115             }
116         }
117         return attributes
118     }
119 
120     private fun createAttrValue(const: Const, expr: Expression?): AnnotationAttributeValue {
121         if (const.kind() == Kind.ARRAY) {
122             const as ArrayInitValue
123             if (const.elements().count() == 1 && expr != null && expr !is ArrayInit) {
124                 // This is case where defined type is array type but provided attribute value is
125                 // single non-array element
126                 // For e.g. @Anno(5) where Anno is @interface Anno {int [] value()}
127                 val constLiteral = const.elements().single()
128                 return DefaultAnnotationSingleAttributeValue(
129                     { TurbineValue(constLiteral, expr).getSourceForAnnotationValue() },
130                     { constLiteral.underlyingValue }
131                 )
132             }
133             return DefaultAnnotationArrayAttributeValue(
134                 { TurbineValue(const, expr).getSourceForAnnotationValue() },
135                 { const.elements().map { createAttrValue(it, null) } }
136             )
137         }
138         return DefaultAnnotationSingleAttributeValue(
139             { TurbineValue(const, expr).getSourceForAnnotationValue() },
140             { const.underlyingValue }
141         )
142     }
143 }
144