<commit_before>/*
 * Copyright (C) 2005-2017 Qihoo 360 Inc.
 *
 * 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.qihoo360.replugin.gradle.plugin.inner

import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.TransformInput
import com.android.build.gradle.internal.scope.GlobalScope
import com.android.sdklib.IAndroidTarget
import com.google.common.reflect.TypeToken
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.stream.JsonReader
import com.qihoo360.replugin.gradle.plugin.AppConstant
import com.qihoo360.replugin.gradle.plugin.manifest.ManifestAPI
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.FileUtils
import com.google.common.base.Charsets
import com.google.common.hash.Hashing
import org.gradle.api.Project

import java.lang.reflect.Field
import java.nio.file.Files
import java.nio.file.Paths
import java.util.zip.ZipFile

import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;

/**
 * @author RePlugin Team
 */
public class Util {

    /** 生成 ClassPool 使用的 ClassPath 集合，同时将要处理的 jar 写入 includeJars */
    def
    static getClassPaths(Project project, String buildType,GlobalScope globalScope, Collection<TransformInput> inputs, Set<String> includeJars, Map<String, String> map) {
        def classpathList = []

        includeJars.clear()

        // android.jar
        classpathList.add(getAndroidJarPath(globalScope))

        // 原始项目中引用的 classpathList
        getProjectClassPath(project, buildType, inputs, includeJars, map).each {
            classpathList.add(it)
        }

        newSection()
        println ">>> ClassPath:"
        classpathList
    }

    /** 获取原始项目中的 ClassPath */
    def private static getProjectClassPath(Project project,String buildType,
                                           Collection<TransformInput> inputs,
                                           Set<String> includeJars, Map<String, String> map) {
        def classPath = []
        def visitor = new ClassFileVisitor()
        def projectDir = project.getRootDir().absolutePath

        println ">>> Unzip Jar ..."
        Map<String, JarPatchInfo> infoMap = readJarInjectorHistory(project, buildType)
        final String injectDir = project.getBuildDir().path +
                File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType;
        def activityList = new ArrayList<>();
        new ManifestAPI().getActivities(project, buildType).each {
            // 处理没有被忽略的 Activity
            if (!(it in CommonData.ignoredActivities)) {
                //
                activityList.add(it)
            }
        }
        Collections.sort(activityList, new Comparator<String>(){
            @Override
            int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        String activityMd5 = DigestUtils.md5Hex(activityList.toString());

        boolean needSave = false
        inputs.each { TransformInput input ->

            input.directoryInputs.each { DirectoryInput dirInput ->
                def dir = dirInput.file.absolutePath
                classPath << dir

                visitor.setBaseDir(dir)
                Files.walkFileTree(Paths.get(dir), visitor)
            }

            input.jarInputs.each { JarInput jarInput ->
                File jar = jarInput.file
                def jarPath = jar.absolutePath
                if (jarPath.contains(File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar")) {
                    //
                }else  if (!jarPath.contains(projectDir)) {
                    String jarZipDir = project.getBuildDir().path +
                            File.separator + FD_INTERMEDIATES + File.separator + "exploded-aar" +
                            File.separator + Hashing.sha1().hashString(jarPath, Charsets.UTF_16LE).toString() + File.separator + "class";
                    if (unzip(jarPath, jarZipDir)) {
                        def jarZip = jarZipDir + ".jar"
                        includeJars << jarPath
                        classPath << jarZipDir
                        visitor.setBaseDir(jarZipDir)
                        Files.walkFileTree(Paths.get(jarZipDir), visitor)
                        map.put(jarPath, jarZip)
                    }
                } else {
                    //重定向jar
                    String md5 = md5File(jar);
                    File reJar = new File(injectDir + File.separator + md5 + ".jar");
                    jarPath = reJar.getAbsolutePath()

                    boolean needInject = false
                    if (reJar.exists()) {
                        //检查修改插件版本
                        JarPatchInfo info = infoMap.get(jar.getAbsolutePath());
                        if (info != null) {
                            if(!activityMd5.equals(info.manifestActivitiesMd5)){
                                needInject = true
                            }else  if (!AppConstant.VER.equals(info.pluginVersion)) {
                                //版本变化了
                                needInject = true
                            } else {
                                if (!md5.equals(info.jarMd5)) {
                                    //原始jar内容变化
                                    needInject = true
                                }
                            }
                        } else {
                            //无记录
                            needInject = true
                        }
                    } else {
                        needInject = true;
                    }
                    //设置重定向jar
                    setJarInput(jarInput, reJar)
                    if (needInject) {
                        /* 将 jar 包解压，并将解压后的目录加入 classpath */
                        // println ">>> 解压Jar${jarPath}"
                        String jarZipDir = reJar.getParent() + File.separatorChar + reJar.getName().replace('.jar', '')
                        if (unzip(jar.getAbsolutePath(), jarZipDir)) {

                            includeJars << jarPath
                            classPath << jarZipDir
                            //保存修改的插件版本号
                            needSave = true
                            infoMap.put(jar.getAbsolutePath(), new JarPatchInfo(jar, activityMd5))

                            visitor.setBaseDir(jarZipDir)
                            Files.walkFileTree(Paths.get(jarZipDir), visitor)

                            map.put(jarPath, jarPath)
                        }
                        // 删除 jar
                        if (reJar.exists()) {
                            FileUtils.forceDelete(reJar)
                        }
                    }
                }
            }
        }
        if (needSave) {
            saveJarInjectorHistory(project, buildType, infoMap)
        }
        return classPath
    }

    /**
     * 计算jar的md5
     */
    def static md5File(File jar) {
        FileInputStream fileInputStream = new FileInputStream(jar);
        String md5 = DigestUtils.md5Hex(fileInputStream);
        fileInputStream.close()
        return md5
    }

    /**
     * 读取修改jar的记录
     */
    def static readJarInjectorHistory(Project project, String buildType) {
        final String dir = FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType;
        File file = new File(project.getBuildDir(), dir + File.separator + "version.json");
        if (!file.exists()) {
            return new HashMap<String, JarPatchInfo>();<commit_msg>aar的jar都一样处理<commit_after>/*
 * Copyright (C) 2005-2017 Qihoo 360 Inc.
 *
 * 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.qihoo360.replugin.gradle.plugin.inner

import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.TransformInput
import com.android.build.gradle.internal.scope.GlobalScope
import com.android.sdklib.IAndroidTarget
import com.google.common.reflect.TypeToken
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.stream.JsonReader
import com.qihoo360.replugin.gradle.plugin.AppConstant
import com.qihoo360.replugin.gradle.plugin.manifest.ManifestAPI
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.FileUtils
import com.google.common.base.Charsets
import com.google.common.hash.Hashing
import org.gradle.api.Project

import java.lang.reflect.Field
import java.nio.file.Files
import java.nio.file.Paths
import java.util.zip.ZipFile

import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;

/**
 * @author RePlugin Team
 */
public class Util {

    /** 生成 ClassPool 使用的 ClassPath 集合，同时将要处理的 jar 写入 includeJars */
    def
    static getClassPaths(Project project, String buildType,GlobalScope globalScope, Collection<TransformInput> inputs, Set<String> includeJars, Map<String, String> map) {
        def classpathList = []

        includeJars.clear()

        // android.jar
        classpathList.add(getAndroidJarPath(globalScope))

        // 原始项目中引用的 classpathList
        getProjectClassPath(project, buildType, inputs, includeJars, map).each {
            classpathList.add(it)
        }

        newSection()
        println ">>> ClassPath:"
        classpathList
    }

    /** 获取原始项目中的 ClassPath */
    def private static getProjectClassPath(Project project,String buildType,
                                           Collection<TransformInput> inputs,
                                           Set<String> includeJars, Map<String, String> map) {
        def classPath = []
        def visitor = new ClassFileVisitor()
        def projectDir = project.getRootDir().absolutePath

        println ">>> Unzip Jar ..."
        Map<String, JarPatchInfo> infoMap = readJarInjectorHistory(project, buildType)
        final String injectDir = project.getBuildDir().path +
                File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType;
        def activityList = new ArrayList<>();
        new ManifestAPI().getActivities(project, buildType).each {
            // 处理没有被忽略的 Activity
            if (!(it in CommonData.ignoredActivities)) {
                //
                activityList.add(it)
            }
        }
        Collections.sort(activityList, new Comparator<String>(){
            @Override
            int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        String activityMd5 = DigestUtils.md5Hex(activityList.toString());

        boolean needSave = false
        inputs.each { TransformInput input ->

            input.directoryInputs.each { DirectoryInput dirInput ->
                def dir = dirInput.file.absolutePath
                classPath << dir

                visitor.setBaseDir(dir)
                Files.walkFileTree(Paths.get(dir), visitor)
            }

            input.jarInputs.each { JarInput jarInput ->
                File jar = jarInput.file
                def jarPath = jar.absolutePath
                if (jarPath.contains(File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar")) {
                    //
                }else{
                    //重定向jar
                    String md5 = md5File(jar);
                    File reJar = new File(injectDir + File.separator + md5 + ".jar");
                    jarPath = reJar.getAbsolutePath()

                    boolean needInject = checkNeedInjector(infoMap, jar, reJar, activityMd5, md5);

                    //设置重定向jar
                    setJarInput(jarInput, reJar)
                    if (needInject) {
                        /* 将 jar 包解压，并将解压后的目录加入 classpath */
                        // println ">>> 解压Jar${jarPath}"
                        String jarZipDir = reJar.getParent() + File.separatorChar + reJar.getName().replace('.jar', '')
                        if (unzip(jar.getAbsolutePath(), jarZipDir)) {

                            includeJars << jarPath
                            classPath << jarZipDir
                            //保存修改的插件版本号
                            needSave = true
                            infoMap.put(jar.getAbsolutePath(), new JarPatchInfo(jar, activityMd5))

                            visitor.setBaseDir(jarZipDir)
                            Files.walkFileTree(Paths.get(jarZipDir), visitor)

                            map.put(jarPath, jarPath)
                        }
                        // 删除 jar
                        if (reJar.exists()) {
                            FileUtils.forceDelete(reJar)
                        }
                    }
                }
            }
        }
        if (needSave) {
            saveJarInjectorHistory(project, buildType, infoMap)
        }
        return classPath
    }

    /**
     * 计算jar的md5
     */
    def static md5File(File jar) {
        FileInputStream fileInputStream = new FileInputStream(jar);
        String md5 = DigestUtils.md5Hex(fileInputStream);
        fileInputStream.close()
        return md5
    }


    def static checkNeedInjector( Map<String, JarPatchInfo> infoMap, File jar,File reJar,String activityMd5,String md5){
        boolean needInject = false
        if (reJar.exists()) {
            //检查修改插件版本
            JarPatchInfo info = infoMap.get(jar.getAbsolutePath());
            if (info != null) {
                if(!activityMd5.equals(info.manifestActivitiesMd5)){
                    needInject = true
                }else  if (!AppConstant.VER.equals(info.pluginVersion)) {
                    //版本变化了
                    needInject = true
                } else {
                    if (!md5.equals(info.jarMd5)) {
                        //原始jar内容变化
                        needInject = true
                    }
                }
            } else {
                //无记录
                needInject = true
            }
        } else {
            needInject = true;
        }
        return needInject;
    }

    /**
     * 读取修改jar的记录
     */
    def static readJarInjectorHistory(Project project, String buildType) {
        final String dir = FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType;
        File file = new File(project.getBuildDir(), dir + File.separator + "version.json");
        if (!file.exists()) {
            return new HashMap<String, JarPatchInfo>();
{'fraction_non_alphanumeric': 0.089594446943891, 'fraction_numerical': 0.00597724789510894, 'mean_word_length': 1.6512182654626002, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2658697', 'n_tokens_mistral': 4286, 'n_tokens_neox': 4124, 'n_words': 878}