blob: eba019a3daed1a217742f5a9451477a0aad1a946 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.build.jetifier.core.type
import com.android.tools.build.jetifier.core.proguard.ProGuardType
import com.android.tools.build.jetifier.core.utils.Log
import java.util.SortedMap
import java.util.regex.Pattern
/**
* Contains all the mappings needed to rewrite java types.
*
* These mappings are generated by the preprocessor from existing support libraries and by applying
* the given [RewriteRule]s.
*/
data class TypesMap(private val types: Map<JavaType, JavaType>) {
companion object {
private const val TAG = "TypesMap"
val EMPTY = TypesMap(emptyMap())
}
init {
val containsNestedTypes = types.any { it.key.hasInnerType() || it.value.hasInnerType() }
if (containsNestedTypes) {
throw IllegalArgumentException("Types map does not support nested types!")
}
}
constructor(vararg types: Pair<JavaType, JavaType>) : this(types.toMap())
/** Returns JSON data model of this class */
fun toJson(): JsonData {
return JsonData(types.map { it.key.fullName to it.value.fullName }.toMap().toSortedMap())
}
/**
* Creates reversed version of this map (values become keys). Throws exception if the map does
* not satisfy that.
*/
fun reverseMapOrDie(): TypesMap {
val typesReversed = mutableMapOf<JavaType, JavaType>()
for ((from, to) in types) {
val conflictFrom = typesReversed[to]
if (conflictFrom != null) {
Log.e(TAG, "Conflict: %s -> (%s, %s)", to, from, conflictFrom)
continue
}
typesReversed[to] = from
}
if (types.size != typesReversed.size) {
throw IllegalArgumentException("Types map is not reversible as conflicts were found! " +
"See the log for more details.")
}
return TypesMap(types = typesReversed)
}
/** Maps the given type using this map. */
fun mapType(type: JavaType): JavaType? {
if (type.hasInnerType()) {
val rootMapResult = types[type.getRootType()] ?: return null
return type.remapWithNewRootType(rootMapResult)
}
return types[type]
}
fun mergeWith(typesMap: TypesMap): TypesMap {
val mergedMap = mutableMapOf<JavaType, JavaType>()
mergedMap.putAll(types)
typesMap.types.forEach {
if (mergedMap.containsKey(it.key)) {
throw RuntimeException("Failed to merge the given types maps as there is" +
" a duplicity with key '${it.key.fullName}' for values '${it.value}' and " +
"'${mergedMap[it.key]}'.")
}
mergedMap.put(it.key, it.value)
}
return TypesMap(mergedMap)
}
/**
* Finds all original types matched by the given ProGuard selector and returns their new types.
*
* Example:
* ProGuard: test.*
* Types: test.Hello => test2.Hello, other.World => other2.World
* Returns: test2.Hello
*/
fun matchOldProguardForNewTypes(proGuardSelector: ProGuardType): Set<JavaType> {
var selector = proGuardSelector.value.replace("?", "[^/]")
selector = selector.replace("*", "@")
selector = selector.replace("@@@", ".*")
selector = selector.replace("@@", ".*")
selector = selector.replace("@", "[^/]*")
val pattern = Pattern.compile(selector)
val foundMatches = mutableSetOf<JavaType>()
types.forEach {
if (pattern.matcher(it.key.fullName).matches()) {
foundMatches.add(it.value)
}
}
return foundMatches
}
/**
* Finds all the types starting with the given prefix.
*/
fun findAllTypesPrefixedWith(prefix: String): Set<JavaType> {
val foundMatches = mutableSetOf<JavaType>()
types.forEach {
if (it.value.fullName.startsWith(prefix)) {
foundMatches.add(it.value)
}
}
return foundMatches
}
/**
* JSON data model for [TypesMap].
*/
data class JsonData(val types: SortedMap<String, String>) {
/** Creates instance of [TypesMap] */
fun toMappings(): TypesMap {
return TypesMap(
types = types
.orEmpty()
.map { JavaType(it.key) to JavaType(it.value) }
.toMap())
}
}
}