• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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.build.jetifier.core.rule
18 
19 import com.android.tools.build.jetifier.core.proguard.ProGuardType
20 import com.android.tools.build.jetifier.core.type.JavaType
21 import com.google.gson.annotations.SerializedName
22 import java.util.regex.Pattern
23 
24 /**
25  * Rule that rewrites a Java type based on the given arguments.
26  *
27  * Used in the preprocessor when generating [TypesMap].
28  *
29  * @param from Regular expression where packages are separated via '/' and inner class separator
30  * is "$". Used to match the input type.
31  * @param to A string to be used as a replacement if the 'from' pattern is matched. It can also
32  * apply groups matched from the original pattern using {x} annotation, e.g. {0}.
33  */
34 class RewriteRule(private val from: String, private val to: String) {
35 
36     companion object {
37         const val IGNORE_RUNTIME = "ignore"
38         const val IGNORE_PREPROCESSOR_ONLY = "ignoreInPreprocessorOnly"
39     }
40 
41     // We escape '$' so we don't conflict with regular expression symbols.
42     private val inputPattern = Pattern.compile("^${from.replace("$", "\\$")}$")
43     private val outputPattern = to.replace("$", "\$")
44 
45     /*
46      * Whether this is any type of an ignore rule.
47      */
isIgnoreRulenull48     fun isIgnoreRule() = isRuntimeIgnoreRule() || isPreprocessorOnlyIgnoreRule()
49 
50     /*
51      * Whether this rules is an ignore rule.
52      *
53      * Any type matched to [from] will be in such case ignored by the preprocessor (thus missing
54      * from the map) but it will be also ignored during rewriting.
55      */
56     fun isRuntimeIgnoreRule() = to == IGNORE_RUNTIME
57 
58     /*
59      * Whether this rule is an ignore rule that should be used only in the preprocessor.
60      *
61      * That means that error is still thrown if [from] is found in a library that is being
62      * rewritten. Use this for types that are internal to support library. This is weaker version of
63      * [isRuntimeIgnoreRule].
64      */
65     fun isPreprocessorOnlyIgnoreRule() = to == IGNORE_PREPROCESSOR_ONLY
66 
67     /**
68      * Rewrites the given java type. Returns null if this rule is not applicable for the given type.
69      */
70     fun apply(input: JavaType): TypeRewriteResult {
71         val matcher = inputPattern.matcher(input.fullName)
72         if (!matcher.matches()) {
73             return TypeRewriteResult.NOT_APPLIED
74         }
75 
76         if (isIgnoreRule()) {
77             return TypeRewriteResult.IGNORED
78         }
79 
80         var result = outputPattern
81         for (i in 0 until matcher.groupCount()) {
82             result = result.replace("{$i}", matcher.group(i + 1))
83         }
84 
85         return TypeRewriteResult(JavaType(result))
86     }
87 
reversenull88     fun reverse(): RewriteRule {
89         val newFrom = to.replace("{0}", "(.*)")
90         val newTo = from.replace("(.*)", "{0}")
91         return RewriteRule(newFrom, newTo)
92     }
93 
94     /*
95      * Returns whether this rule is an ignore rule and applies to the given proGuard type.
96      */
doesThisIgnoreProGuardnull97     fun doesThisIgnoreProGuard(type: ProGuardType): Boolean {
98         if (!isIgnoreRule()) {
99             return false
100         }
101 
102         val matcher = inputPattern.matcher(type.value)
103         return matcher.matches()
104     }
105 
toStringnull106     override fun toString(): String {
107         return "$inputPattern -> $outputPattern "
108     }
109 
110     /** Returns JSON data model of this class */
toJsonnull111     fun toJson(): JsonData {
112         return JsonData(from, to)
113     }
114 
115     /**
116      * JSON data model for [RewriteRule].
117      */
118     data class JsonData(
119         @SerializedName("from")
120         val from: String,
121 
122         @SerializedName("to")
123         val to: String) {
124 
125         /** Creates instance of [RewriteRule] */
toRulenull126         fun toRule(): RewriteRule {
127             return RewriteRule(from, to)
128         }
129     }
130 
131     /**
132      * Result of java type rewrite using [RewriteRule]
133      */
134     data class TypeRewriteResult(val result: JavaType?, val isIgnored: Boolean = false) {
135 
136         companion object {
137             val NOT_APPLIED = TypeRewriteResult(result = null, isIgnored = false)
138 
139             val IGNORED = TypeRewriteResult(result = null, isIgnored = true)
140         }
141     }
142 }