Java tutorial
/* * 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())); } }