1 /*
2  * Copyright (C) 2017 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
18 
19 import com.android.tools.metalava.model.visitors.ItemVisitor
20 import com.android.tools.metalava.model.visitors.TypeVisitor
21 import com.intellij.psi.PsiElement
22 
23 /**
24  * Represents a code element such as a package, a class, a method, a field, a parameter.
25  *
26  * This extra abstraction on top of PSI allows us to more model the API (and customize
27  * visibility, which cannot always be done by looking at a particular piece of code and examining
28  * visibility and @hide/@removed annotations: sometimes package private APIs are unhidden by
29  * being used in public APIs for example.
30  *
31  * The abstraction also lets us back the model by an alternative implementation read from
32  * signature files, to do compatibility checks.
33  * */
34 interface Item {
35     val codebase: Codebase
36 
37     /** Return the modifiers of this class */
38     val modifiers: ModifierList
39 
40     /**
41      * Whether this element should be part of the API. The algorithm for this is complicated, so it can't
42      * be computed initially; we'll make passes over the source code to determine eligibility and mark all
43      * items as included or not.
44      */
45     var included: Boolean
46 
47     /** Whether this element has been hidden with @hide/@Hide (or after propagation, in some containing class/pkg) */
48     var hidden: Boolean
49 
50     var emit: Boolean
51 
parentnull52     fun parent(): Item?
53 
54     /** Recursive check to see if this item or any of its parents (containing class, containing package) are hidden */
55     fun hidden(): Boolean {
56         return hidden || parent()?.hidden() ?: false
57     }
58 
59     /** Whether this element has been removed with @removed/@Remove (or after propagation, in some containing class) */
60     var removed: Boolean
61 
62     /** True if this element has been marked deprecated */
63     val deprecated: Boolean
64 
65     /** True if this element is only intended for documentation */
66     var docOnly: Boolean
67 
68     /** True if this item is either hidden or removed */
isHiddenOrRemovednull69     fun isHiddenOrRemoved(): Boolean = hidden || removed
70 
71     /** Visits this element using the given [visitor] */
72     fun accept(visitor: ItemVisitor)
73 
74     /** Visits all types in this item hierarchy */
75     fun acceptTypes(visitor: TypeVisitor)
76 
77     /** Get a mutable version of modifiers for this item */
78     fun mutableModifiers(): MutableModifierList
79 
80     /** The javadoc/KDoc comment for this code element, if any. This is
81      * the original content of the documentation, including lexical tokens
82      * to begin, continue and end the comment (such as /+*).
83      * See [fullyQualifiedDocumentation] to look up the documentation with
84      * fully qualified references to classes.
85      */
86     var documentation: String
87 
88     /** Looks up docs for a specific tag */
89     fun findTagDocumentation(tag: String): String?
90 
91     /**
92      * A rank used for sorting. This allows signature files etc to
93      * sort similar items by a natural order, if non-zero.
94      * (Even though in signature files the elements are normally
95      * sorted first logically (constructors, then methods, then fields)
96      * and then alphabetically, this lets us preserve the source
97      * ordering for example for overloaded methods of the same name,
98      * where it's not clear that an alphabetical order (of each
99      * parameter?) would be preferable.)
100      */
101     val sortingRank: Int
102 
103     /**
104      * Add the given text to the documentation.
105      *
106      * If the [tagSection] is null, add the comment to the initial text block
107      * of the description. Otherwise if it is "@return", add the comment
108      * to the return value. Otherwise the [tagSection] is taken to be the
109      * parameter name, and the comment added as parameter documentation
110      * for the given parameter.
111      */
112     fun appendDocumentation(comment: String, tagSection: String? = null, append: Boolean = true)
113 
114     val isPublic: Boolean
115     val isProtected: Boolean
116     val isPackagePrivate: Boolean
117     val isPrivate: Boolean
118 
119     // make sure these are implemented so we can place in maps:
120     override fun equals(other: Any?): Boolean
121 
122     override fun hashCode(): Int
123 
124     /** Whether this member was cloned in from a super class or interface */
125     fun isCloned(): Boolean
126 
127     /**
128      * Returns true if this item requires nullness information (e.g. for a method
129      * where either the return value or any of the parameters are non-primitives.
130      * Note that it doesn't consider whether it already has nullness annotations;
131      * for that see [hasNullnessInfo].
132      */
133     fun requiresNullnessInfo(): Boolean = false
134 
135     /**
136      * Returns true if this item requires nullness information and supplies it
137      * (for all items, e.g. if a method is partially annotated this method would
138      * still return false)
139      */
140     fun hasNullnessInfo(): Boolean = false
141 
142     /**
143      * Whether this item was loaded from the classpath (e.g. jar dependencies)
144      * rather than be declared as source
145      */
146     fun isFromClassPath(): Boolean = false
147 
148     /** Is this element declared in Java (rather than Kotlin) ? */
149     fun isJava(): Boolean = true
150 
151     /** Is this element declared in Kotlin (rather than Java) ? */
152     fun isKotlin() = !isJava()
153 
154     fun hasShowAnnotation(): Boolean = modifiers.hasShowAnnotation()
155     fun hasHideAnnotation(): Boolean = modifiers.hasHideAnnotations()
156 
157     fun checkLevel(): Boolean {
158         return modifiers.checkLevel()
159     }
160 
compilationUnitnull161     fun compilationUnit(): CompilationUnit? {
162         var curr: Item? = this
163         while (curr != null) {
164             if (curr is ClassItem && curr.isTopLevelClass()) {
165                 return curr.getCompilationUnit()
166             }
167             curr = curr.parent()
168         }
169 
170         return null
171     }
172 
173     /** Returns the PSI element for this item, if any */
psinull174     fun psi(): PsiElement? = null
175 
176     /** Tag field used for DFS etc */
177     var tag: Boolean
178 
179     /**
180      * Returns the [documentation], but with fully qualified links (except for the same package, and
181      * when turning a relative reference into a fully qualified reference, use the javadoc syntax
182      * for continuing to display the relative text, e.g. instead of {@link java.util.List}, use
183      * {@link java.util.List List}.
184      */
185     fun fullyQualifiedDocumentation(): String = documentation
186 }
187 
188 abstract class DefaultItem(override val sortingRank: Int = nextRank++) : Item {
189     override val isPublic: Boolean get() = modifiers.isPublic()
190     override val isProtected: Boolean get() = modifiers.isProtected()
191     override val isPackagePrivate: Boolean get() = modifiers.isPackagePrivate()
192     override val isPrivate: Boolean get() = modifiers.isPrivate()
193 
194     override var emit = true
195     override var tag: Boolean = false
196 
197     // TODO: Get rid of this; with the new predicate approach it's redundant (and
198     // storing it per element is problematic since the predicate sometimes includes
199     // methods from parent interfaces etc)
200     override var included: Boolean = true
201 
202     companion object {
203         private var nextRank: Int = 1
204     }
205 }