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
18 
19 import com.android.tools.metalava.model.ClassItem
20 import com.android.tools.metalava.model.ConstructorItem
21 import com.android.tools.metalava.model.FieldItem
22 import com.android.tools.metalava.model.Item
23 import com.android.tools.metalava.model.MethodItem
24 import com.android.tools.metalava.model.ParameterItem
25 import com.android.tools.metalava.model.TypeItem
26 import com.android.tools.metalava.model.VisibilityLevel
27 import com.android.tools.metalava.model.visitors.ApiVisitor
28 import java.io.PrintWriter
29 import java.util.function.Predicate
30 
31 class ProguardWriter(
32     private val writer: PrintWriter,
33     filterEmit: Predicate<Item>,
34     filterReference: Predicate<Item>
35 ) : ApiVisitor(
36     visitConstructorsAsMethods = false,
37     nestInnerClasses = false,
38     inlineInheritedFields = true,
39     filterEmit = filterEmit,
40     filterReference = filterReference
41 ) {
42 
visitClassnull43     override fun visitClass(cls: ClassItem) {
44         writer.print("-keep class ")
45         writer.print(cls.qualifiedNameWithDollarInnerClasses())
46         writer.print(" {\n")
47     }
48 
afterVisitClassnull49     override fun afterVisitClass(cls: ClassItem) {
50         writer.print("}\n")
51     }
52 
visitConstructornull53     override fun visitConstructor(constructor: ConstructorItem) {
54         writer.print("    ")
55         writer.print("<init>")
56 
57         writeParametersKeepList(constructor.parameters())
58         writer.print(";\n")
59     }
60 
visitMethodnull61     override fun visitMethod(method: MethodItem) {
62         writer.print("    ")
63         val modifiers = method.modifiers
64         val visibilityLevel = modifiers.getVisibilityLevel()
65         if (visibilityLevel != VisibilityLevel.PACKAGE_PRIVATE) {
66             writer.write(visibilityLevel.sourceCodeModifier + " ")
67         }
68 
69         if (modifiers.isStatic()) {
70             writer.print("static ")
71         }
72         if (modifiers.isAbstract()) {
73             writer.print("abstract ")
74         }
75         if (modifiers.isSynchronized()) {
76             writer.print("synchronized ")
77         }
78 
79         writer.print(getCleanTypeName(method.returnType()))
80         writer.print(" ")
81         writer.print(method.name())
82 
83         writeParametersKeepList(method.parameters())
84 
85         writer.print(";\n")
86     }
87 
writeParametersKeepListnull88     private fun writeParametersKeepList(params: List<ParameterItem>) {
89         writer.print("(")
90 
91         for (pi in params) {
92             if (pi !== params[0]) {
93                 writer.print(", ")
94             }
95             writer.print(getCleanTypeName(pi.type()))
96         }
97 
98         writer.print(")")
99     }
100 
visitFieldnull101     override fun visitField(field: FieldItem) {
102         writer.print("    ")
103 
104         val modifiers = field.modifiers
105         val visibilityLevel = modifiers.getVisibilityLevel()
106         if (visibilityLevel != VisibilityLevel.PACKAGE_PRIVATE) {
107             writer.write(visibilityLevel.sourceCodeModifier + " ")
108         }
109 
110         if (modifiers.isStatic()) {
111             writer.print("static ")
112         }
113         if (modifiers.isTransient()) {
114             writer.print("transient ")
115         }
116         if (modifiers.isVolatile()) {
117             writer.print("volatile ")
118         }
119 
120         writer.print(getCleanTypeName(field.type()))
121 
122         writer.print(" ")
123         writer.print(field.name())
124 
125         writer.print(";\n")
126     }
127 
getCleanTypeNamenull128     private fun getCleanTypeName(t: TypeItem?): String {
129         t ?: return ""
130         val cls = t.asClass() ?: return t.toCanonicalType()
131         var qualifiedName = cls.qualifiedNameWithDollarInnerClasses()
132 
133         for (i in 0 until t.arrayDimensions()) {
134             qualifiedName += "[]"
135         }
136         return qualifiedName
137     }
138 }