1 /*
2  * Copyright (C) 2018 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.text
18 
19 import com.android.tools.metalava.model.ClassItem
20 import com.android.tools.metalava.model.DefaultModifierList
21 import com.android.tools.metalava.model.TypeParameterItem
22 import com.android.tools.metalava.model.TypeParameterListOwner
23 
24 class TextTypeParameterItem(
25     codebase: TextCodebase,
26     private val owner: TypeParameterListOwner?,
27     private val typeParameterString: String,
28     name: String,
29     private var bounds: List<ClassItem>? = null
30 ) :
31     TextClassItem(
32         codebase = codebase,
33         modifiers = TextModifiers(codebase, DefaultModifierList.PUBLIC),
34         name = name,
35         qualifiedName = name
36     ),
37     TypeParameterItem {
38 
boundsnull39     override fun bounds(): List<ClassItem> {
40         if (bounds == null) {
41             val boundsString = bounds(typeParameterString, owner)
42             bounds = if (boundsString.isEmpty()) {
43                 emptyList()
44             } else {
45                 boundsString.mapNotNull {
46                     val clz = codebase.findClass(it)
47                     if (clz == null && it.contains(".")) {
48                         codebase.getOrCreateClass(it)
49                     } else {
50                         clz
51                     }
52                 }.filter {
53                     !it.isJavaLangObject()
54                 }
55             }
56         }
57         return bounds!!
58     }
59 
isReifiednull60     override fun isReified(): Boolean {
61         return typeParameterString.startsWith("reified")
62     }
63 
64     companion object {
createnull65         fun create(
66             codebase: TextCodebase,
67             owner: TypeParameterListOwner?,
68             typeParameterString: String,
69             bounds: List<ClassItem>? = null
70         ): TextTypeParameterItem {
71             val length = typeParameterString.length
72             var nameEnd = length
73             for (i in 0 until length) {
74                 val c = typeParameterString[i]
75                 if (!Character.isJavaIdentifierPart(c)) {
76                     nameEnd = i
77                     break
78                 }
79             }
80             val name = typeParameterString.substring(0, nameEnd)
81             return TextTypeParameterItem(
82                 codebase = codebase,
83                 owner = owner,
84                 typeParameterString = typeParameterString,
85                 name = name,
86                 bounds = bounds
87             )
88         }
89 
boundsnull90         fun bounds(typeString: String?, owner: TypeParameterListOwner? = null): List<String> {
91             val s = typeString ?: return emptyList()
92             val index = s.indexOf("extends ")
93             if (index == -1) {
94                 // See if this is a type variable that has bounds in the parent
95                 val parameters = (owner as? TextMemberItem)?.containingClass()?.typeParameterList()?.typeParameters()
96                     ?: return emptyList()
97                 for (p in parameters) {
98                     if (p.simpleName() == s) {
99                         return p.bounds().filter { !it.isJavaLangObject() }.map { it.qualifiedName() }
100                     }
101                 }
102 
103                 return emptyList()
104             }
105             val list = mutableListOf<String>()
106             var balance = 0
107             var start = index + "extends ".length
108             val length = s.length
109             for (i in start until length) {
110                 val c = s[i]
111                 if (c == '&' && balance == 0) {
112                     add(list, typeString, start, i)
113                     start = i + 1
114                 } else if (c == '<') {
115                     balance++
116                     if (balance == 1) {
117                         add(list, typeString, start, i)
118                     }
119                 } else if (c == '>') {
120                     balance--
121                     if (balance == 0) {
122                         start = i + 1
123                     }
124                 }
125             }
126             if (start < length) {
127                 add(list, typeString, start, length)
128             }
129             return list
130         }
131 
addnull132         private fun add(list: MutableList<String>, s: String, from: Int, to: Int) {
133             for (i in from until to) {
134                 if (!Character.isWhitespace(s[i])) {
135                     var end = to
136                     while (end > i && s[end - 1].isWhitespace()) {
137                         end--
138                     }
139                     var begin = i
140                     while (begin < end && s[begin].isWhitespace()) {
141                         begin++
142                     }
143                     if (begin == end) {
144                         return
145                     }
146                     val element = s.substring(begin, end)
147                     list.add(element)
148                     return
149                 }
150             }
151         }
152     }
153 }