From fa94a442cbf980ec9cfdefedfeddcaee9f20125b Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Sun, 17 May 2020 19:56:57 +0200 Subject: [PATCH] Introduce regexp-based filtering for decompiled file names. Resolves #14 --- README.md | 67 ++++++++++--------- .../com/github/kwart/jd/cli/CLIArguments.java | 8 +++ .../java/com/github/kwart/jd/cli/Main.java | 10 +-- jd-cli/src/main/resources/logback.xml | 19 +++--- jd-lib/pom.xml | 13 ++++ .../kwart/jd/input/AbstractFileJDInput.java | 21 +++++- .../github/kwart/jd/input/ClassFileInput.java | 9 ++- .../com/github/kwart/jd/input/DirInput.java | 11 ++- .../github/kwart/jd/input/ZipFileInput.java | 14 +++- .../github/kwart/jd/JavaDecompilerTest.java | 60 ++++++++++++----- jd-lib/src/test/resources/logback.xml | 13 ++++ 11 files changed, 173 insertions(+), 72 deletions(-) create mode 100644 jd-lib/src/test/resources/logback.xml diff --git a/README.md b/README.md index 6cde456..317bc12 100644 --- a/README.md +++ b/README.md @@ -15,38 +15,41 @@ Find latest bits in **[GitHub Releases](https://github.com/kwart/jd-cmd/releases You can use the `jd-cli.bat` (Windows) or `jd-cli` (Linux/Unix) scripts to run the the JAR file. Usage: java -jar jd-cli.jar [options] [Files to decompile] - Options: - --displayLineNumbers, -n - displays line numbers in decompiled classes - Default: false - --escapeUnicodeCharacters, -eu - escape unicode characters in decompiled classes - Default: false - --help, -h - shows this help - Default: false - --logLevel, -g - takes [level] as parameter and sets it as the CLI log level. Possible - values are: ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF - Default: INFO - --outputConsole, -oc - enables output to system output stream - Default: false - --outputDir, -od - takes a [directoryPath] as a parameter and configures a flat DIR output - for this path - --outputDirStructured, -ods - takes a [directoryPath] as a parameter and configures a structured DIR - output for this path - --outputZipFile, -oz - takes a [zipFilePath] as a parameter and configures ZIP output for this - path - --skipResources, -sr - skips processing resources - Default: false - --version, -v - shows the version - Default: false + Options: + --displayLineNumbers, -n + displays line numbers in decompiled classes + Default: false + --escapeUnicodeCharacters, -eu + escape unicode characters in decompiled classes + Default: false + --help, -h + shows this help + Default: false + --logLevel, -g + takes [level] as parameter and sets it as the CLI log level. Possible + values are: ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF + Default: INFO + --outputConsole, -oc + enables output to system output stream + Default: false + --outputDir, -od + takes a [directoryPath] as a parameter and configures a flat DIR output + for this path + --outputDirStructured, -ods + takes a [directoryPath] as a parameter and configures a structured DIR + output for this path + --outputZipFile, -oz + takes a [zipFilePath] as a parameter and configures ZIP output for this + path + --pattern, -p + RegExp pattern which the to-be-decompiled file has to match. Not matching + entries are skipped. + --skipResources, -sr + skips processing resources + Default: false + --version, -v + shows the version + Default: false ## Credits diff --git a/jd-cli/src/main/java/com/github/kwart/jd/cli/CLIArguments.java b/jd-cli/src/main/java/com/github/kwart/jd/cli/CLIArguments.java index 2b99db1..19348d1 100644 --- a/jd-cli/src/main/java/com/github/kwart/jd/cli/CLIArguments.java +++ b/jd-cli/src/main/java/com/github/kwart/jd/cli/CLIArguments.java @@ -73,6 +73,10 @@ public class CLIArguments implements DecompilerOptions { converter = LogLevelConverter.class) private final Level logLevel = Level.INFO; + @Parameter(names = { "--pattern", "-p" }, description = + "RegExp pattern which the to-be-decompiled file has to match. Not matching entries are skipped.") + private final String pattern = null; + public List getFiles() { return files; } @@ -117,6 +121,10 @@ public Level getLogLevel() { return logLevel; } + public String getPattern() { + return pattern; + } + public boolean isOutputPluginSpecified() { return consoleOut || zipOutFile != null || dirOutFile != null || dirOutFileStructured != null; } diff --git a/jd-cli/src/main/java/com/github/kwart/jd/cli/Main.java b/jd-cli/src/main/java/com/github/kwart/jd/cli/Main.java index aece903..1095f54 100755 --- a/jd-cli/src/main/java/com/github/kwart/jd/cli/Main.java +++ b/jd-cli/src/main/java/com/github/kwart/jd/cli/Main.java @@ -106,7 +106,7 @@ public static void main(String[] args) { if (file.exists()) { try { - InputOutputPair inOut = getInOutPlugins(file, outputPlugin); + InputOutputPair inOut = getInOutPlugins(file, outputPlugin, cliArguments.getPattern()); inOut.getJdInput().decompile(javaDecompiler, inOut.getJdOutput()); decompiled = true; } catch (Exception e) { @@ -200,12 +200,12 @@ private static JDOutput initOutput(final CLIArguments cliArguments) { * @throws NullPointerException * @throws IOException */ - public static InputOutputPair getInOutPlugins(final File inputFile, JDOutput outPlugin) + public static InputOutputPair getInOutPlugins(final File inputFile, JDOutput outPlugin, String pattern) throws NullPointerException, IOException { JDInput jdIn = null; JDOutput jdOut = null; if (inputFile.isDirectory()) { - jdIn = new DirInput(inputFile.getPath()); + jdIn = new DirInput(inputFile.getPath(), pattern); jdOut = new DirOutput(new File(inputFile.getName() + ".src")); } else { DataInputStream dis = new DataInputStream(new FileInputStream(inputFile)); @@ -217,11 +217,11 @@ public static InputOutputPair getInOutPlugins(final File inputFile, JDOutput out } switch (magic) { case JavaDecompilerConstants.MAGIC_NR_CLASS_FILE: - jdIn = new ClassFileInput(inputFile.getPath()); + jdIn = new ClassFileInput(inputFile.getPath(), pattern); jdOut = new PrintStreamOutput(System.out); break; case JavaDecompilerConstants.MAGIC_NR_ZIP_FILE: - jdIn = new ZipFileInput(inputFile.getPath()); + jdIn = new ZipFileInput(inputFile.getPath(), pattern); String decompiledZipName = inputFile.getName(); int suffixPos = decompiledZipName.lastIndexOf("."); if (suffixPos >= 0) { diff --git a/jd-cli/src/main/resources/logback.xml b/jd-cli/src/main/resources/logback.xml index fabfc17..3f10124 100644 --- a/jd-cli/src/main/resources/logback.xml +++ b/jd-cli/src/main/resources/logback.xml @@ -1,12 +1,13 @@ - + - - - %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n - - + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + - - - + + + \ No newline at end of file diff --git a/jd-lib/pom.xml b/jd-lib/pom.xml index 641d0a3..e768d84 100644 --- a/jd-lib/pom.xml +++ b/jd-lib/pom.xml @@ -26,6 +26,19 @@ jd-core ${jd.version} + + + ch.qos.logback + logback-core + ${logback.version} + test + + + ch.qos.logback + logback-classic + ${logback.version} + test + diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/AbstractFileJDInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/AbstractFileJDInput.java index 62ef837..d09f527 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/AbstractFileJDInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/AbstractFileJDInput.java @@ -31,6 +31,7 @@ public abstract class AbstractFileJDInput implements JDInput { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFileJDInput.class); protected final File file; + private final String pattern; /** * Constructor based on an existing file path. @@ -38,11 +39,27 @@ public abstract class AbstractFileJDInput implements JDInput { * @param filePath path to input file * @throws IllegalArgumentException path doesn't denote an existing file */ - public AbstractFileJDInput(final String filePath) throws IllegalArgumentException { - LOGGER.trace("Creating JDInput instance for file {}", filePath); + public AbstractFileJDInput(String filePath) throws IllegalArgumentException { + this(filePath, null); + } + + public AbstractFileJDInput(String filePath, String pattern) throws IllegalArgumentException { + LOGGER.trace("Creating JDInput instance for file {} and pattern {}", filePath, pattern); + this.pattern = pattern == null ? pattern : (".*" + pattern + ".*"); file = new File(filePath); if (!file.exists()) { throw new IllegalArgumentException("Path doesn't denote an existing file."); } } + + /** + * Returns {@code true} when a pattern is configured and given path doesn't match it. + */ + protected boolean skipThePath(String path) { + boolean skip = pattern != null && path != null && !path.matches(pattern); + if (skip) { + LOGGER.debug("Skipping the path {} as it doesn't match the pattern {}", path, pattern); + } + return skip; + } } diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java index e090d76..a59ac75 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java @@ -37,6 +37,10 @@ public ClassFileInput(String path) { super(path); } + public ClassFileInput(String filePath, String pattern) throws IllegalArgumentException { + super(filePath, pattern); + } + /* * (non-Javadoc) * @@ -49,8 +53,11 @@ public void decompile(JavaDecompiler javaDecompiler, JDOutput jdOutput) { return; } - jdOutput.init(javaDecompiler.getOptions(), ""); final String name = file.getName(); + if (skipThePath(name)) { + return; + } + jdOutput.init(javaDecompiler.getOptions(), ""); LOGGER.debug("Decompiling single class file {}", name); String nameWithoutClassSfx = IOUtils.isClassFile(name) ? IOUtils.cutClassSuffix(name) : name; jdOutput.processClass(nameWithoutClassSfx, javaDecompiler.decompileClass(new FileLoader(), file.getPath())); diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java index d072959..59e6ae0 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java @@ -38,14 +38,18 @@ public class DirInput extends AbstractFileJDInput { private static final Logger LOGGER = LoggerFactory.getLogger(DirInput.class); private final FileLoader fileLoader; - public DirInput(String path) { - super(path); + public DirInput(String path, String pattern) throws IllegalArgumentException { + super(path, pattern); if (!file.isDirectory()) { throw new IllegalArgumentException("Path doesn't denote a directory."); } this.fileLoader = new FileLoader(path); } + public DirInput(String path) { + this(path, null); + } + @Override public void decompile(JavaDecompiler javaDecompiler, JDOutput jdOutput) { if (javaDecompiler == null || jdOutput == null) { @@ -70,6 +74,9 @@ private void processFile(JavaDecompiler javaDecompiler, JDOutput jdOutput, Strin processFile(javaDecompiler, jdOutput, pathPrefix + fileName + "/", f); } } else { + if (skipThePath(nameWithPath)) { + return; + } if (IOUtils.isClassFile(fileName)) { if (IOUtils.isInnerClass(fileName)) { // don't handle inner classes diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/ZipFileInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/ZipFileInput.java index fcb56de..2b3b189 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/ZipFileInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/ZipFileInput.java @@ -40,14 +40,19 @@ public class ZipFileInput extends AbstractFileJDInput { private static final Logger LOGGER = LoggerFactory.getLogger(ZipFileInput.class); /** - * Constructor which takes - * - * @param path + * Constructor. */ public ZipFileInput(String path) { super(path); } + /** + * Constructor. + */ + public ZipFileInput(String filePath, String pattern) throws IllegalArgumentException { + super(filePath, pattern); + } + /** * Parses all entres in the zip and decompiles it writing results to {@link JDOutput} instance. * @@ -73,6 +78,9 @@ public void decompile(JavaDecompiler javaDecompiler, JDOutput jdOutput) { while ((entry = zis.getNextEntry()) != null) { if (!entry.isDirectory()) { final String entryName = entry.getName(); + if (skipThePath(entryName)) { + continue; + } if (IOUtils.isClassFile(entryName)) { if (IOUtils.isInnerClass(entryName)) { // don't handle inner classes diff --git a/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java b/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java index 05a7cea..c4b32c4 100644 --- a/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java +++ b/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java @@ -1,6 +1,7 @@ package com.github.kwart.jd; import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -28,6 +29,24 @@ */ public class JavaDecompilerTest { + private static final DecompilerOptions DC_OPTS = new DecompilerOptions() { + + @Override + public boolean isSkipResources() { + return false; + } + + @Override + public boolean isEscapeUnicodeCharacters() { + return false; + } + + @Override + public boolean isDisplayLineNumbers() { + return false; + } + }; + /** * Temporary folder. */ @@ -35,7 +54,7 @@ public class JavaDecompilerTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test - public void test() throws LoaderException, IOException { + public void basicTest() throws LoaderException, IOException { JDInput input = new DirInput("target/test-classes"); File tmpRootFolder = temporaryFolder.getRoot(); File tmpFolder = new File(tmpRootFolder, "flat"); @@ -44,28 +63,33 @@ public void test() throws LoaderException, IOException { outPlugins.add(new DirOutput(tmpFolder.getAbsoluteFile())); outPlugins.add(new StructuredDirOutput(tmpStructuredFolder.getAbsoluteFile())); JDOutput output = new MultiOutput(outPlugins); - JavaDecompiler decompiler = new JavaDecompiler(new DecompilerOptions() { - - @Override - public boolean isSkipResources() { - return false; - } - - @Override - public boolean isEscapeUnicodeCharacters() { - return false; - } - - @Override - public boolean isDisplayLineNumbers() { - return false; - } - }); + JavaDecompiler decompiler = new JavaDecompiler(DC_OPTS); input.decompile(decompiler, output); assertDecompiled(tmpFolder); assertDecompiled(new File(tmpStructuredFolder, "test-classes")); } + @Test + public void patternNotMatchingTest() throws LoaderException, IOException { + JDInput input = new DirInput("target/test-classes", "Not.*Matching"); + File tmpFolder = temporaryFolder.getRoot(); + JDOutput output = new DirOutput(tmpFolder.getAbsoluteFile()); + JavaDecompiler decompiler = new JavaDecompiler(DC_OPTS); + input.decompile(decompiler, output); + File decompiledFile = new File(tmpFolder, "com/github/kwart/jd/HelloWorld.java"); + assertFalse(decompiledFile.isFile()); + } + + @Test + public void patternMatchingTest() throws LoaderException, IOException { + JDInput input = new DirInput("target/test-classes", "jd.Hello.*World\\."); + File tmpFolder = temporaryFolder.getRoot(); + JDOutput output = new DirOutput(tmpFolder.getAbsoluteFile()); + JavaDecompiler decompiler = new JavaDecompiler(DC_OPTS); + input.decompile(decompiler, output); + assertDecompiled(tmpFolder); + } + private void assertDecompiled(File outputFolder) throws IOException { File decompiledFile = new File(outputFolder, "com/github/kwart/jd/HelloWorld.java"); assertTrue(decompiledFile.isFile()); diff --git a/jd-lib/src/test/resources/logback.xml b/jd-lib/src/test/resources/logback.xml new file mode 100644 index 0000000..18b97e8 --- /dev/null +++ b/jd-lib/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file