Home | History | Annotate | Download | only in model
      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