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