com.j2swift.util.ProGuardUsageParser.java Source code

Java tutorial

Introduction

Here is the source code for com.j2swift.util.ProGuardUsageParser.java

Source

/*
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * 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.j2swift.util;

import com.google.common.io.CharSource;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parses dead code reports generated by ProGuard.
 *
 * Example ProGuard configuration file to generate an acceptable listing:
 *
 * <pre><code>
 * -injars <foo.jar>
 * -libraryjars <java.home>/lib/rt.jar
 *
 * -dontoptimize
 * -dontobfuscate
 * -dontpreverify
 * -printusage
 * -dontnote
 *
 * -keep public class com.google.foo.Bar {
 *     public static void main(java.lang.String[]);
 * }
 *
 * -keepclassmembers class * {
 *     static final % *;
 *     static final java.lang.String *;
 * }
 * </code></pre>
 *
 * @author Daniel Connelly
 */
public class ProGuardUsageParser {

    private static final Pattern proGuardMethodPattern = Pattern.compile("    " + // leading indent
            "(\\d+:\\d+:)?" + // method line numbers (optional)
            "((public|private|protected|static|synchronized|varargs|bridge|"
            + "native|abstract|strictfp|final|synthetic)\\s)*" + // keywords
            "((\\S+)\\s)?(\\S+)\\((\\S*)\\)"); // return type, method name, arguments

    private ProGuardUsageParser() {
        // Don't instantiate.
    }

    private static String buildTypeSignature(String type) {
        if (type.equals("byte")) {
            return "B";
        }
        if (type.equals("char")) {
            return "C";
        }
        if (type.equals("double")) {
            return "D";
        }
        if (type.equals("float")) {
            return "F";
        }
        if (type.equals("int")) {
            return "I";
        }
        if (type.equals("long")) {
            return "J";
        }
        if (type.equals("short")) {
            return "S";
        }
        if (type.equals("boolean")) {
            return "Z";
        }
        if (type.equals("void")) {
            return "V";
        }
        if (type.endsWith("[]")) {
            return "[" + buildTypeSignature(type.substring(0, type.length() - 2));
        }
        if (type.length() == 0) {
            return "";
        }
        return "L" + type.replace('.', '/') + ";";
    }

    private static String buildMethodSignature(String returnType, String argumentList) {
        String[] arguments = argumentList.split(",");
        StringBuilder signature = new StringBuilder().append("(");
        for (String argument : arguments) {
            signature.append(buildTypeSignature(argument));
        }
        signature.append(")");
        signature.append(buildTypeSignature(returnType != null ? returnType : "void"));
        return signature.toString();
    }

    public static DeadCodeMap parse(CharSource listing) throws IOException {
        LineProcessor<DeadCodeMap> processor = new LineProcessor<DeadCodeMap>() {
            DeadCodeMap.Builder dead = DeadCodeMap.builder();
            String lastClass;

            @Override
            public DeadCodeMap getResult() {
                return dead.build();
            }

            private void handleClass(String line) {
                if (line.endsWith(":")) {
                    // Class, but not completely dead; save to read its dead methods
                    lastClass = line.substring(0, line.length() - 1);
                } else {
                    dead.addDeadClass(line);
                }
            }

            private void handleMethod(String line) throws IOException {
                Matcher methodMatcher = proGuardMethodPattern.matcher(line);
                if (!methodMatcher.matches()) {
                    throw new AssertionError("Line doesn't match expected ProGuard format!");
                }
                if (lastClass == null) {
                    throw new IOException("Bad listing format: method not attached to a class");
                }
                String returnType = methodMatcher.group(5);
                String methodName = methodMatcher.group(6);
                String arguments = methodMatcher.group(7);
                String signature = buildMethodSignature(returnType, arguments);
                dead.addDeadMethod(lastClass, methodName, signature);
            }

            private void handleField(String line) throws IOException {
                String name = line.substring(line.lastIndexOf(" ") + 1);
                dead.addDeadField(lastClass, name);
            }

            @Override
            public boolean processLine(String line) throws IOException {
                if (line.startsWith("ProGuard, version") || line.startsWith("Reading ")) {
                    // ignore output header
                } else if (!line.startsWith("    ")) {
                    handleClass(line);
                } else if (line.startsWith("    ") && !line.contains("(")) {
                    handleField(line);
                } else {
                    handleMethod(line);
                }
                return true;
            }
        };

        // TODO(cgdecker): Just use listing.readLines(processor) once guava_jdk5 is upgraded to a newer
        // version.
        Closer closer = Closer.create();
        try {
            BufferedReader reader = closer.register(listing.openBufferedStream());
            String line;
            while ((line = reader.readLine()) != null) {
                processor.processLine(line);
            }
            return processor.getResult();
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    // Used for testing.
    public static void main(String[] args) throws IOException {
        ProGuardUsageParser.parse(Files.asCharSource(new File(args[0]), Charset.defaultCharset()));
    }

}