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.DocLevel 20 import com.android.tools.metalava.DocLevel.HIDDEN 21 import com.android.tools.metalava.DocLevel.PACKAGE 22 import com.android.tools.metalava.DocLevel.PRIVATE 23 import com.android.tools.metalava.DocLevel.PROTECTED 24 import com.android.tools.metalava.DocLevel.PUBLIC 25 import com.android.tools.metalava.Options 26 import com.android.tools.metalava.compatibility 27 import com.android.tools.metalava.options 28 import java.io.Writer 29 30 interface ModifierList { 31 val codebase: Codebase 32 fun annotations(): List<AnnotationItem> 33 34 fun owner(): Item 35 fun isPublic(): Boolean 36 fun isProtected(): Boolean 37 fun isPrivate(): Boolean 38 fun isStatic(): Boolean 39 fun isAbstract(): Boolean 40 fun isFinal(): Boolean 41 fun isNative(): Boolean 42 fun isSynchronized(): Boolean 43 fun isStrictFp(): Boolean 44 fun isTransient(): Boolean 45 fun isVolatile(): Boolean 46 fun isDefault(): Boolean 47 48 // Modifier in Kotlin, separate syntax (...) in Java but modeled as modifier here 49 fun isVarArg(): Boolean = false 50 51 // Kotlin 52 fun isSealed(): Boolean = false 53 54 fun isInternal(): Boolean = false 55 fun isInfix(): Boolean = false 56 fun isOperator(): Boolean = false 57 fun isInline(): Boolean = false 58 fun isEmpty(): Boolean 59 60 fun isPackagePrivate() = !(isPublic() || isProtected() || isPrivate()) 61 62 // Rename? It's not a full equality, it's whether an override's modifier set is significant 63 fun equivalentTo(other: ModifierList): Boolean { 64 if (isPublic() != other.isPublic()) return false 65 if (isProtected() != other.isProtected()) return false 66 if (isPrivate() != other.isPrivate()) return false 67 68 if (isStatic() != other.isStatic()) return false 69 if (isAbstract() != other.isAbstract()) return false 70 if (isFinal() != other.isFinal()) return false 71 if (!compatibility.skipNativeModifier && isNative() != other.isNative()) return false 72 if (isSynchronized() != other.isSynchronized()) return false 73 if (!compatibility.skipStrictFpModifier && isStrictFp() != other.isStrictFp()) return false 74 if (isTransient() != other.isTransient()) return false 75 if (isVolatile() != other.isVolatile()) return false 76 77 // Default does not require an override to "remove" it 78 // if (isDefault() != other.isDefault()) return false 79 80 return true 81 } 82 83 /** Returns true if this modifier list contains any nullness information */ 84 fun hasNullnessInfo(): Boolean { 85 return annotations().any { it.isNonNull() || it.isNullable() } 86 } 87 88 /** 89 * Returns true if this modifier list contains any annotations explicitly passed in 90 * via [Options.showAnnotations] 91 */ 92 fun hasShowAnnotation(): Boolean { 93 if (options.showAnnotations.isEmpty()) { 94 return false 95 } 96 return annotations().any { 97 options.showAnnotations.contains(it.qualifiedName()) 98 } 99 } 100 101 /** 102 * Returns true if this modifier list contains any annotations explicitly passed in 103 * via [Options.hideAnnotations] 104 */ 105 fun hasHideAnnotations(): Boolean { 106 if (options.hideAnnotations.isEmpty()) { 107 return false 108 } 109 return annotations().any { 110 options.hideAnnotations.contains(it.qualifiedName()) 111 } 112 } 113 114 /** Returns true if this modifier list contains the given annotation */ 115 fun isAnnotatedWith(qualifiedName: String): Boolean { 116 return findAnnotation(qualifiedName) != null 117 } 118 119 /** Returns the annotation of the given qualified name if found in this modifier list */ 120 fun findAnnotation(qualifiedName: String): AnnotationItem? { 121 val mappedName = AnnotationItem.mapName(codebase, qualifiedName) 122 return annotations().firstOrNull { 123 mappedName == it.qualifiedName() 124 } 125 } 126 127 /** Returns true if this modifier list has adequate access */ 128 fun checkLevel() = checkLevel(options.docLevel) 129 130 /** 131 * Returns true if this modifier list has access modifiers that 132 * are adequate for the given documentation level 133 */ 134 fun checkLevel(level: DocLevel): Boolean { 135 if (level == HIDDEN) { 136 return true 137 } else if (owner().isHiddenOrRemoved()) { 138 return false 139 } 140 return when (level) { 141 PUBLIC -> isPublic() 142 PROTECTED -> isPublic() || isProtected() 143 PACKAGE -> !isPrivate() 144 PRIVATE, HIDDEN -> true 145 } 146 } 147 148 /** 149 * Returns true if the visibility modifiers in this modifier list is as least as visible 150 * as the ones in the given [other] modifier list 151 */ 152 fun asAccessibleAs(other: ModifierList): Boolean { 153 return when { 154 other.isPublic() -> isPublic() 155 other.isProtected() -> isPublic() || isProtected() 156 other.isPackagePrivate() -> isPublic() || isProtected() || isPackagePrivate() 157 other.isInternal() -> isPublic() || isProtected() || isInternal() 158 other.isPrivate() -> true 159 else -> true 160 } 161 } 162 163 fun getVisibilityString(): String { 164 return when { 165 isPublic() -> "public" 166 isProtected() -> "protected" 167 isPackagePrivate() -> "package private" 168 isInternal() -> "internal" 169 isPrivate() -> "private" 170 else -> error(toString()) 171 } 172 } 173 174 companion object { 175 fun write( 176 writer: Writer, 177 modifiers: ModifierList, 178 item: Item, 179 // TODO: "deprecated" isn't a modifier; clarify method name 180 includeDeprecated: Boolean = false, 181 includeAnnotations: Boolean = true, 182 skipNullnessAnnotations: Boolean = false, 183 omitCommonPackages: Boolean = false, 184 removeAbstract: Boolean = false, 185 removeFinal: Boolean = false, 186 addPublic: Boolean = false, 187 onlyIncludeSignatureAnnotations: Boolean = true 188 189 ) { 190 191 val list = if (removeAbstract || removeFinal || addPublic) { 192 class AbstractFiltering : ModifierList by modifiers { 193 override fun isAbstract(): Boolean { 194 return if (removeAbstract) false else modifiers.isAbstract() 195 } 196 197 override fun isFinal(): Boolean { 198 return if (removeFinal) false else modifiers.isFinal() 199 } 200 201 override fun isPublic(): Boolean { 202 return if (addPublic) true else modifiers.isPublic() 203 } 204 } 205 AbstractFiltering() 206 } else { 207 modifiers 208 } 209 210 if (includeAnnotations) { 211 writeAnnotations( 212 list = list, 213 skipNullnessAnnotations = skipNullnessAnnotations, 214 omitCommonPackages = omitCommonPackages, 215 separateLines = false, 216 writer = writer, 217 onlyIncludeSignatureAnnotations = onlyIncludeSignatureAnnotations 218 ) 219 } 220 221 if (compatibility.doubleSpaceForPackagePrivate && item.isPackagePrivate && item is MemberItem) { 222 writer.write(" ") 223 } 224 225 // Kotlin order: 226 // https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers 227 228 // Abstract: should appear in interfaces if in compat mode 229 val classItem = item as? ClassItem 230 val methodItem = item as? MethodItem 231 232 // Order based on the old stubs code: TODO, use Java standard order instead? 233 234 if (compatibility.nonstandardModifierOrder) { 235 when { 236 list.isPublic() -> writer.write("public ") 237 list.isProtected() -> writer.write("protected ") 238 list.isInternal() -> writer.write("internal ") 239 list.isPrivate() -> writer.write("private ") 240 } 241 242 if (list.isDefault()) { 243 writer.write("default ") 244 } 245 246 if (list.isStatic()) { 247 writer.write("static ") 248 } 249 250 if (list.isFinal() && 251 // Don't show final on parameters: that's an implementation side detail 252 item !is ParameterItem && 253 (classItem?.isEnum() != true || compatibility.finalInInterfaces) || 254 compatibility.forceFinalInEnumValueMethods && 255 methodItem?.name() == "values" && methodItem.containingClass().isEnum() 256 ) { 257 writer.write("final ") 258 } 259 260 if (list.isSealed()) { 261 writer.write("sealed ") 262 } 263 264 if (list.isInfix()) { 265 writer.write("infix ") 266 } 267 268 if (list.isOperator()) { 269 writer.write("operator ") 270 } 271 272 val isInterface = classItem?.isInterface() == true || 273 (methodItem?.containingClass()?.isInterface() == true && 274 !list.isDefault() && !list.isStatic()) 275 276 if ((compatibility.abstractInInterfaces && isInterface || 277 list.isAbstract() && 278 (classItem?.isEnum() != true && 279 (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) && 280 (!isInterface || compatibility.abstractInInterfaces) 281 ) { 282 writer.write("abstract ") 283 } 284 285 if (!compatibility.skipNativeModifier && list.isNative()) { 286 writer.write("native ") 287 } 288 289 if (item.deprecated && includeDeprecated) { 290 writer.write("deprecated ") 291 } 292 293 if (list.isSynchronized()) { 294 writer.write("synchronized ") 295 } 296 297 if (!compatibility.skipStrictFpModifier && list.isStrictFp()) { 298 writer.write("strictfp ") 299 } 300 301 if (list.isTransient()) { 302 writer.write("transient ") 303 } 304 305 if (list.isVolatile()) { 306 writer.write("volatile ") 307 } 308 } else { 309 if (item.deprecated && includeDeprecated) { 310 writer.write("deprecated ") 311 } 312 313 when { 314 list.isPublic() -> writer.write("public ") 315 list.isProtected() -> writer.write("protected ") 316 list.isInternal() -> writer.write("internal ") 317 list.isPrivate() -> writer.write("private ") 318 } 319 320 val isInterface = classItem?.isInterface() == true || 321 (methodItem?.containingClass()?.isInterface() == true && 322 !list.isDefault() && !list.isStatic()) 323 324 if ((compatibility.abstractInInterfaces && isInterface || 325 list.isAbstract() && 326 (classItem?.isEnum() != true && 327 (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) && 328 (!isInterface || compatibility.abstractInInterfaces) 329 ) { 330 writer.write("abstract ") 331 } 332 333 if (list.isDefault() && item !is ParameterItem) { 334 writer.write("default ") 335 } 336 337 if (list.isStatic()) { 338 writer.write("static ") 339 } 340 341 if (list.isFinal() && 342 // Don't show final on parameters: that's an implementation side detail 343 item !is ParameterItem && 344 (classItem?.isEnum() != true || compatibility.finalInInterfaces) 345 ) { 346 writer.write("final ") 347 } 348 349 if (list.isSealed()) { 350 writer.write("sealed ") 351 } 352 353 if (list.isInfix()) { 354 writer.write("infix ") 355 } 356 357 if (list.isOperator()) { 358 writer.write("operator ") 359 } 360 361 if (list.isTransient()) { 362 writer.write("transient ") 363 } 364 365 if (list.isVolatile()) { 366 writer.write("volatile ") 367 } 368 369 if (list.isSynchronized()) { 370 writer.write("synchronized ") 371 } 372 373 if (!compatibility.skipNativeModifier && list.isNative()) { 374 writer.write("native ") 375 } 376 377 if (!compatibility.skipStrictFpModifier && list.isStrictFp()) { 378 writer.write("strictfp ") 379 } 380 } 381 } 382 383 fun writeAnnotations( 384 list: ModifierList, 385 skipNullnessAnnotations: Boolean = false, 386 omitCommonPackages: Boolean = false, 387 separateLines: Boolean = false, 388 filterDuplicates: Boolean = false, 389 writer: Writer, 390 onlyIncludeSignatureAnnotations: Boolean = true 391 ) { 392 val annotations = list.annotations() 393 if (annotations.isNotEmpty()) { 394 var index = -1 395 for (annotation in annotations) { 396 index++ 397 if ((annotation.isNonNull() || annotation.isNullable())) { 398 if (skipNullnessAnnotations) { 399 continue 400 } 401 } else if (onlyIncludeSignatureAnnotations && !annotation.isSignificant()) { 402 continue 403 } 404 405 // Optionally filter out duplicates 406 if (index > 0 && filterDuplicates) { 407 val qualifiedName = annotation.qualifiedName() 408 var found = false 409 for (i in 0 until index) { 410 val prev = annotations[i] 411 if (prev.qualifiedName() == qualifiedName) { 412 found = true 413 break 414 } 415 } 416 if (found) { 417 continue 418 } 419 } 420 421 val source = annotation.toSource() 422 if (omitCommonPackages) { 423 writer.write(AnnotationItem.shortenAnnotation(source)) 424 } else { 425 writer.write(source) 426 } 427 if (separateLines) { 428 writer.write("\n") 429 } else { 430 writer.write(" ") 431 } 432 } 433 } 434 } 435 } 436 } 437