All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.therouter.plugin.TheRouterTransform.groovy Maven / Gradle / Ivy
package com.therouter.plugin
import com.android.build.api.transform.*
import com.android.build.gradle.internal.pipeline.TransformManager
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import org.apache.commons.io.FileUtils
import org.gradle.api.Project
/**
* Created by ZhangTao on 18/2/24.
*/
public class TheRouterTransform extends Transform {
private Project mProject
private final Set allClass = new HashSet<>()
private final Set deletedClass = new HashSet<>()
public TheRouterTransform(Project p) {
this.mProject = p
}
@Override
String getName() {
return "TheRouter"
}
@Override
Set getInputTypes() {
return TransformManager.CONTENT_CLASS
}
@Override
Set getScopes() {
return TransformManager.SCOPE_FULL_PROJECT
}
@Override
boolean isIncremental() {
return Boolean.valueOf(getLocalProperty(TheRouterPlugin.INCREMENTAL))
}
@Override
void transform(Context context, Collection inputs,
Collection referencedInputs,
TransformOutputProvider outputProvider,
boolean isIncremental)
throws IOException, javax.xml.crypto.dsig.TransformException, InterruptedException {
theRouterTransform(isIncremental, inputs, outputProvider)
}
private void theRouterTransform(boolean isIncremental, Collection inputs, outputProvider) {
println("TheRouter编译插件:${LogUI.C_BLACK_GREEN.value}" + "cn.therouter:${BuildConfig.NAME}:${BuildConfig.VERSION}" + "${LogUI.E_NORMAL.value}")
println "当前编译 JDK Version 为::" + System.getProperty("java.version")
println "本次是增量编译::" + isIncremental
println "CHECK_ROUTE_MAP::" + getLocalProperty(TheRouterPlugin.CHECK_ROUTE_MAP)
println "CHECK_FLOW_UNKNOW_DEPEND::" + getLocalProperty(TheRouterPlugin.CHECK_FLOW_UNKNOW_DEPEND)
long startFirst = System.currentTimeMillis()
long start = System.currentTimeMillis()
def theRouterClassOutputFile
Set routeMapStringSet = new HashSet<>();
Map flowTaskMap = new HashMap<>();
println("---------TheRouter transform start-------------------------------------------")
if (!isIncremental) {
outputProvider.deleteAll()
}
inputs.each { TransformInput input ->
// 遍历jar包
input.jarInputs.each { JarInput jarInput ->
def jarName = jarInput.name.toLowerCase()
def dest = outputProvider.getContentLocation(jarName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
if (!isIncremental) {
if (!jarName.contains("com.google.") &&
!jarName.contains("org.jetbrains") &&
!jarName.contains("androidx.") &&
!jarName.contains("io.reactivex") &&
!jarName.contains("com.squareup") &&
!jarName.contains("glide") &&
!jarName.contains("upppay") &&
!jarName.contains("amap") &&
!jarName.contains("hllim") &&
!jarName.contains("pinyin4j") &&
!jarName.contains("sensors")) {
JarInfo jarInfo = TheRouterInjects.tagJar(jarInput.file)
routeMapStringSet.addAll(jarInfo.routeMapStringFromJar)
flowTaskMap.putAll(jarInfo.flowTaskMapFromJar)
allClass.addAll(jarInfo.allJarClass)
if (jarInfo.isTheRouterJar) {
theRouterClassOutputFile = dest
}
}
if (dest.exists()) {
FileUtils.forceDelete(dest)
}
FileUtils.copyFile(jarInput.file, dest)
} else if (isIncremental && (jarInput.getStatus() == Status.CHANGED || jarInput.getStatus() == Status.ADDED)) {
JarInfo jarInfo = TheRouterInjects.tagJar(jarInput.file)
routeMapStringSet.addAll(jarInfo.routeMapStringFromJar)
flowTaskMap.putAll(jarInfo.flowTaskMapFromJar)
FileUtils.copyFile(jarInput.file, dest)
} else if (isIncremental && jarInput.getStatus() == Status.REMOVED) {
JarInfo jarInfo = TheRouterInjects.tagJar(jarInput.file)
deletedClass.addAll(jarInfo.allJarClass)
if (dest.exists()) {
FileUtils.forceDelete(dest)
}
}
}
// 遍历源码
input.directoryInputs.each { DirectoryInput directoryInput ->
File dir = directoryInput.file
def dest = outputProvider.getContentLocation(directoryInput.name,
directoryInput.contentTypes, directoryInput.scopes,
Format.DIRECTORY)
FileUtils.forceMkdir(dest)
String srcDirPath = dir.absolutePath
String destDirPath = dest.absolutePath
if (isIncremental) {
Map fileStatusMap = directoryInput.getChangedFiles()
for (Map.Entry changedFile : fileStatusMap.entrySet()) {
Status status = changedFile.getValue()
File inputFile = changedFile.getKey()
String destFilePath = inputFile.absolutePath.replace(srcDirPath, destDirPath)
File destFile = new File(destFilePath)
switch (status) {
case Status.REMOVED:
deletedClass.add(inputFile.absolutePath.replaceAll("/", "."))
if (destFile.exists()) {
destFile.delete()
}
break
case Status.ADDED:
case Status.CHANGED:
SourceInfo sourceInfo = TheRouterInjects.tagClass(inputFile.absolutePath)
routeMapStringSet.addAll(sourceInfo.routeMapStringFromSource)
flowTaskMap.putAll(sourceInfo.flowTaskMapFromSource)
FileUtils.copyFile(inputFile, destFile)
break
default:
break
}
}
} else {
SourceInfo sourceInfo = TheRouterInjects.tagClass(directoryInput.file.absolutePath)
allClass.addAll(sourceInfo.allSourceClass)
routeMapStringSet.addAll(sourceInfo.routeMapStringFromSource)
flowTaskMap.putAll(sourceInfo.flowTaskMapFromSource)
if (dest.exists()) {
FileUtils.forceDelete(dest)
}
FileUtils.copyDirectory(directoryInput.file, dest)
}
}
}
if (theRouterClassOutputFile) {
TheRouterInjects.injectClassCode(theRouterClassOutputFile, isIncremental)
}
long time = System.currentTimeMillis() - start;
println("---------TheRouter ASM, spend:${time}ms----------------------")
start = System.currentTimeMillis()
Set pageSet = new HashSet<>()
Gson gson = new GsonBuilder().setPrettyPrinting().create()
routeMapStringSet.each {
pageSet.addAll((List) gson.fromJson(it, new TypeToken>() {
}.getType()))
}
// 让第三方Activity也支持路由,第三方页面的路由表可以在assets中添加
File assetRouteMap = new File(mProject.projectDir, "src/main/assets/therouter/routeMap.json")
if (assetRouteMap.exists()) {
String assetString = FileUtils.readFileToString(assetRouteMap)
println("---------TheRouter get route map from: /assets/therouter/routeMap.json-------")
try {
List assetsList = (List) gson.fromJson(assetString, new TypeToken>() {
}.getType())
for (RouteItem item : assetsList) {
if (!pageSet.contains(item)) {
pageSet.add(item)
}
}
} catch (Exception e) {
e.printStackTrace()
}
} else {
println("---------TheRouter route map does not exist: /assets/therouter/routeMap.json-------")
assetRouteMap.getParentFile().mkdirs()
assetRouteMap.createNewFile()
}
Map> result = new HashMap<>()
// 检查url合法性
pageSet.each { routeItem ->
String url = routeItem.path
if (url.contains("?")) {
URI uri = new URI(routeItem.path)
def map = uri.getProperties()
for (key in map.keySet()) {
routeItem.params.put(key, map.get(key))
}
url = url.substring(0, url.indexOf('?'))
}
List routeList = result.get(url)
if (routeList == null) {
routeList = new ArrayList<>()
routeList.add(routeItem)
result.put(url, routeList)
}
}
// 检查路由表合法性
result.values().each {
String className = null
it.each { routeItem ->
if (className == null) {
className = routeItem.className
} else if (className != routeItem.className) {
throw new RuntimeException("Multiple Activity to single Url: $className and ${routeItem.className}")
}
if (!getLocalProperty(TheRouterPlugin.CHECK_ROUTE_MAP).isEmpty()) {
// 只在全量编译时检验
if (!isIncremental) {
boolean classNotFound = true
for (String item : allClass) {
if (item.contains(routeItem.className)) {
classNotFound = false
break
}
}
if (classNotFound) {
if (TheRouterPlugin.ERROR.equalsIgnoreCase(getLocalProperty(TheRouterPlugin.CHECK_ROUTE_MAP))) {
throw new ClassNotFoundException(routeItem.className + " in /assets/therouter/routeMap.json")
} else if (TheRouterPlugin.WARNING.equalsIgnoreCase(getLocalProperty(TheRouterPlugin.CHECK_ROUTE_MAP))) {
println("${LogUI.C_WARN.value}[${routeItem.className} in /assets/therouter/routeMap.json]${LogUI.E_NORMAL.value}")
}
}
}
}
}
}
List pageList
if (isIncremental && !deletedClass.isEmpty()) {
pageList = new ArrayList<>(pageSet.size())
for (RouteItem item : pageSet) {
boolean needDelete = false
for (String deletedClassItem : deletedClass) {
if (deletedClassItem.contains(item.className)) {
needDelete = true
break
}
}
if (!needDelete) {
pageList.add(item)
}
}
} else {
pageList = new ArrayList<>(pageSet)
}
Collections.sort(pageList)
String json = gson.toJson(pageList)
FileUtils.write(assetRouteMap, json, false)
time = System.currentTimeMillis() - start
println("---------TheRouter create new route map, spend:${time}ms--------------")
start = System.currentTimeMillis()
Map> flowTaskDependMap = new HashMap<>();
flowTaskMap.keySet().each {
Set value = flowTaskDependMap.get(it)
if (value == null) {
value = new HashSet<>()
}
String dependsOn = flowTaskMap.get(it)
if (!dependsOn.isBlank()) {
dependsOn.split(",").each { depend ->
if (!depend.isBlank()) {
value.add(depend.trim())
}
}
}
flowTaskDependMap.put(it, value)
}
if (!getLocalProperty(TheRouterPlugin.CHECK_FLOW_UNKNOW_DEPEND).isEmpty()) {
flowTaskDependMap.values().each { taskName ->
flowTaskDependMap[taskName].each {
if (!flowTaskDependMap.containsKey(it)) {
if (TheRouterPlugin.ERROR.equalsIgnoreCase(getLocalProperty(TheRouterPlugin.CHECK_FLOW_UNKNOW_DEPEND))) {
throw new RuntimeException("\n\n==========================================" +
"\nTheRouter:: FlowTask:: " +
"\nCan not found Task: [$it] from $taskName dependsOn" +
"\n==========================================\n\n")
} else if (TheRouterPlugin.ERROR.equalsIgnoreCase(getLocalProperty(TheRouterPlugin.CHECK_FLOW_UNKNOW_DEPEND))) {
println()
println("${LogUI.C_WARN.value}" + "==========================================" + "${LogUI.E_NORMAL.value}")
println("${LogUI.C_WARN.value}" + "TheRouter:: FlowTask:: " + "${LogUI.E_NORMAL.value}")
println("${LogUI.C_WARN.value}" + "Can not found Task: [$it] from $taskName dependsOn" + "${LogUI.E_NORMAL.value}")
println("${LogUI.C_WARN.value}" + "==========================================" + "${LogUI.E_NORMAL.value}")
println()
}
}
}
}
}
flowTaskDependMap.keySet().each {
fillTodoList(flowTaskDependMap, it)
}
if (Boolean.valueOf(getLocalProperty(TheRouterPlugin.SHOW_FLOW_DEPEND))) {
flowTaskDependMap.keySet().each {
fillNode(createNode(flowTaskDependMap, it), null)
}
println()
println("${LogUI.C_WARN.value}" + "TheRouter:: FlowTask::dependency " + "${LogUI.E_NORMAL.value}")
println("${LogUI.C_WARN.value}" + "==========================================" + "${LogUI.E_NORMAL.value}")
dependStack.sort().each {
println("${LogUI.C_WARN.value}" + "[Root --> $it]" + "${LogUI.E_NORMAL.value}")
}
println("${LogUI.C_WARN.value}" + "==========================================" + "${LogUI.E_NORMAL.value}")
println()
}
time = System.currentTimeMillis() - start
println("---------TheRouter check flow task map, spend:${time}ms--------------")
time = System.currentTimeMillis() - startFirst;
println("---------TheRouter transform finish, spend:${time}ms------------------------------------------")
}
private final List loopDependStack = new ArrayList<>()
private void fillTodoList(Map> map, String root) {
Set dependsSet = map[root]
if (dependsSet != null && !dependsSet.isEmpty()) {
if (loopDependStack.contains(root)) {
throw new RuntimeException("\n\n==========================================" +
"\nTheRouter:: FlowTask:: " +
"\nCyclic dependency: [${getLog(loopDependStack, root)}]" +
"\n==========================================\n\n")
}
loopDependStack.add(root)
for (depend in dependsSet) {
fillTodoList(map, depend)
}
loopDependStack.remove(root)
}
}
Set dependStack = new HashSet<>()
private void fillNode(Node node, String root) {
if (node.children == null || node.children.isEmpty()) {
if (root == null) {
dependStack.add(node.name)
} else {
dependStack.add(node.name + " --> " + root)
}
} else {
node.children.each {
if (root == null) {
fillNode(it, node.name)
} else {
fillNode(it, node.name + " --> " + root)
}
}
}
}
private Node createNode(Map> map, String root) {
Node node = new Node(root)
Set childrenNode = new HashSet<>()
Set dependsSet = map[root]
if (dependsSet != null && !dependsSet.isEmpty()) {
for (depend in dependsSet) {
childrenNode.add(createNode(map, depend))
}
}
node.children = childrenNode
return node
}
private String getLog(List list, String root) {
if (list == null || list.isEmpty()) {
return ""
}
StringBuilder stringBuilder = new StringBuilder()
for (task in list) {
stringBuilder.append(task).append("-->")
}
if (root != null) {
stringBuilder.append(root)
}
return stringBuilder.toString()
}
def getLocalProperty(String key) {
try {
def value = getLocalProperties().getProperty(key)
return value == null ? "" : value
} catch (Exception e) {
e.printStackTrace()
return ""
}
}
def getLocalProperties() {
def properties = new Properties()
try {
File localPropertiesFile
try {
localPropertiesFile = new File(mProject.rootDir, 'local.properties');
if (localPropertiesFile == null || !localPropertiesFile.exists()) {
localPropertiesFile = new File("../local.properties")
}
} catch (Exception e) {
localPropertiesFile = new File("../local.properties")
}
properties.load(new FileInputStream(localPropertiesFile))
return properties
} catch (Exception e) {
e.printStackTrace()
return properties
}
}
}