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 }