<commit_before>/*
 * 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 info.persistent.dex;

import com.android.dexdeps.DexData;
import com.android.dexdeps.DexDataException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

public class Main {
    private boolean countFields;
    private boolean includeClasses;
    private String packageFilter;
    private int maxDepth = Integer.MAX_VALUE;
    private DexMethodCounts.Filter filter = DexMethodCounts.Filter.ALL;
    private DexMethodCounts.OutputStyle outputStyle = DexMethodCounts.OutputStyle.TREE;


    public static void main(String[] args) {
        Main main = new Main();
        main.run(args);
    }


    void run(String[] args) {
        try {
            String[] inputFileNames = parseArgs(args);
            int overallCount = 0;
            for (String fileName : collectFileNames(inputFileNames)) {
                System.out.println("Processing " + fileName);
                DexCount counts;
                if (countFields) {
                    counts = new DexFieldCounts(outputStyle);
                } else {
                    counts = new DexMethodCounts(outputStyle);
                }
                List<RandomAccessFile> dexFiles = openInputFiles(fileName);

                for (RandomAccessFile dexFile : dexFiles) {
                    DexData dexData = new DexData(dexFile);
                    dexData.load();
                    counts.generate(dexData, includeClasses, packageFilter, maxDepth, filter);
                    dexFile.close();
                }
                counts.output();
                overallCount = counts.getOverallCount();
            }
            System.out.println(
                String.format("Overall %s count: %d", countFields ? "field" : "method",
                    overallCount));
        } catch (UsageException ue) {
            usage();
            System.exit(2);
        } catch (IOException ioe) {
            if (ioe.getMessage() != null) {
                System.err.println("Failed: " + ioe);
            }
            System.exit(1);
        } catch (DexDataException dde) {
            /* a message was already reported, just bail quietly */
            System.exit(1);
        }
    }


    /**
     * Opens an input file, which could be a .dex or a .jar/.apk with a
     * classes.dex inside.  If the latter, we extract the contents to a
     * temporary file.
     */
    List<RandomAccessFile> openInputFiles(String fileName) throws IOException {
        List<RandomAccessFile> dexFiles = new ArrayList<RandomAccessFile>();

        openInputFileAsZip(fileName, dexFiles);
        if (dexFiles.size() == 0) {
            File inputFile = new File(fileName);
            RandomAccessFile dexFile = new RandomAccessFile(inputFile, "r");
            dexFiles.add(dexFile);
        }

        return dexFiles;
    }


    /**
     * Tries to open an input file as a Zip archive (jar/apk) with a
     * "classes.dex" inside.
     */
    void openInputFileAsZip(String fileName, List<RandomAccessFile> dexFiles) throws IOException {
        ZipFile zipFile;

        // Try it as a zip file.
        try {
            zipFile = new ZipFile(fileName);
        } catch (FileNotFoundException fnfe) {
            // not found, no point in retrying as non-zip.
            System.err.println("Unable to open '" + fileName + "': " +
                fnfe.getMessage());
            throw fnfe;
        } catch (ZipException ze) {
            // not a zip
            return;
        }

        // Open and add all files matching "classes.*\.dex" in the zip file.
        for (ZipEntry entry : Collections.list(zipFile.entries())) {
            if (entry.getName().matches("classes.*\\.dex")) {
                dexFiles.add(openDexFile(zipFile, entry));
            }
        }

        zipFile.close();
    }


    RandomAccessFile openDexFile(ZipFile zipFile, ZipEntry entry) throws IOException {
        // We know it's a zip; see if there's anything useful inside.  A
        // failure here results in some type of IOException (of which<commit_msg>[Add] comments of Main class in dex-method-counts module<commit_after>/*
 * 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 info.persistent.dex;

import com.android.dexdeps.DexData;
import com.android.dexdeps.DexDataException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

@SuppressWarnings("DanglingJavadoc")
public class Main {
    private boolean countFields;
    private boolean includeClasses;
    private String packageFilter;
    private int maxDepth = Integer.MAX_VALUE;
    private DexMethodCounts.Filter filter = DexMethodCounts.Filter.ALL;
    private DexMethodCounts.OutputStyle outputStyle = DexMethodCounts.OutputStyle.TREE;


    public static void main(String[] args) {
        Main main = new Main();
        main.run(args);
    }


    void run(String[] args) {
        try {
            /**
             * 解析 参数
             * 获取一些解析选项 和 路径
             */
            String[] inputFileNames = parseArgs(args);
            int overallCount = 0;
            /**
             * 遍历所有传入的路径
             * 获取每个路径文件（ dex or apk ），文件内的每个 （ dex ）
             *
             * 遍历每个 dex 数据
             * 通过 google 的 api，实例化 DexData
             * 通过 google 的 api，生成 所有 dex 数据（ class，package，maxDepth and filter ）
             *
             * 输出方法数信息
             *
             * 获取方法数
             */
            for (String fileName : collectFileNames(inputFileNames)) {
                System.out.println("Processing " + fileName);
                DexCount counts;
                if (countFields) {
                    counts = new DexFieldCounts(outputStyle);
                } else {
                    counts = new DexMethodCounts(outputStyle);
                }
                // 路径文件（ dex or apk ），文件内的每个 （ dex ）
                List<RandomAccessFile> dexFiles = openInputFiles(fileName);

                /**
                 * 遍历每个 dex 数据
                 * 通过 google 的 api，实例化 DexData
                 * 通过 google 的 api，生成 所有 dex 数据（ class，package，maxDepth and filter ）
                 */
                for (RandomAccessFile dexFile : dexFiles) {
                    DexData dexData = new DexData(dexFile);
                    dexData.load();
                    counts.generate(dexData, includeClasses, packageFilter, maxDepth, filter);
                    dexFile.close();
                }
                // 输出方法数信息
                counts.output();
                // 获取方法数
                overallCount = counts.getOverallCount();
            }
            System.out.println(
                String.format("Overall %s count: %d", countFields ? "field" : "method",
                    overallCount));
        } catch (UsageException ue) {
            usage();
            System.exit(2);
        } catch (IOException ioe) {
            if (ioe.getMessage() != null) {
                System.err.println("Failed: " + ioe);
            }
            System.exit(1);
        } catch (DexDataException dde) {
            /* a message was already reported, just bail quietly */
            System.exit(1);
        }
    }


    /**
     * Opens an input file, which could be a .dex or a .jar/.apk with a
     * classes.dex inside.  If the latter, we extract the contents to a
     * temporary file.
     *
     * 将路径转为 zip
     * 获取 zip 文件中的每个 dex 的数据（  RandomAccessFile ）
     * 返回一个  List<RandomAccessFile>
     */
    List<RandomAccessFile> openInputFiles(String fileName) throws IOException {
        List<RandomAccessFile> dexFiles = new ArrayList<RandomAccessFile>();

        openInputFileAsZip(fileName, dexFiles);
        if (dexFiles.size() == 0) {
            File inputFile = new File(fileName);
            RandomAccessFile dexFile = new RandomAccessFile(inputFile, "r");
            dexFiles.add(dexFile);
        }

        return dexFiles;
    }


    /**
     * Tries to open an input file as a Zip archive (jar/apk) with a
     * "classes.dex" inside.
     *
     * 将 路径 转换为 zip
     *
     * 遍历 zip 文件中的每个 dex
     * 通过 openDexFile 方法获取 zip 文件中的每个 dex 的数据（ RandomAccessFile ）
     * 保存到集合 dexFiles
     */
    void openInputFileAsZip(String fileName, List<RandomAccessFile> dexFiles) throws IOException {
        ZipFile zipFile;

        // Try it as a zip file.
        try {
            zipFile = new ZipFile(fileName);
        } catch (FileNotFoundException fnfe) {
            // not found, no point in retrying as non-zip.
            System.err.println("Unable to open '" + fileName + "': " +
                fnfe.getMessage());
            throw fnfe;
        } catch (ZipException ze) {
            // not a zip
            return;
        }

        // Open and add all files matching "classes.*\.dex" in the zip file.
        for (ZipEntry entry : Collections.list(zipFile.entries())) {
            if (entry.getName().matches("classes.*\\.dex")) {
                dexFiles.add(openDexFile(zipFile, entry));
            }
        }

        zipFile.close();
    }


    /**
     * 读取 zip 中的 dex 数据
     *
     * 其实就是创建了一个 new dex
     * 然后用 RandomAccessFile 把 zip 中的 dex 的数据写到 new dex 上
     * 最后删除这个 new dex，因为数据已经读完到了 RandomAccessFile 上
     * 不需要这个 new dex
     *
     * 返回的是 RandomAccessFile，已经将 zip 的数据读完了
     *
     * @param zipFile zipFile
     * @param entry entry
     * @return RandomAccessFile
     * @throws IOException IOException
     */
    RandomAccessFile openDexFile(ZipFile zipFile, ZipEntry entry) throws IOException {
        // We know it's a zip; see if there's anything useful inside.  A
        // failure here results in some type of IOException (of which
{'fraction_non_alphanumeric': 0.09229534510433386, 'fraction_numerical': 0.0016051364365971107, 'mean_word_length': 1.925906600573963, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5323897', 'n_tokens_mistral': 3268, 'n_tokens_neox': 3156, 'n_words': 904}