├── .gitignore ├── .idea ├── .gitignore ├── encodings.xml ├── jpa-buddy.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── assets ├── demodiff.png ├── image-20230919170844682.png ├── image-20230919171001889.png ├── image-20230919171727105.png ├── image-20230926115942438.png └── image-20230926120322733.png ├── pom.xml ├── readme-zh.md ├── readme.md └── src └── main └── java └── org └── bridge ├── Config.java ├── Main.java ├── core ├── decompile │ ├── Decompiler.java │ └── JDCoreDecompiler.java ├── differ │ └── DiffFileNodeInfo.java ├── filetree │ ├── DeltaCommons.java │ ├── FileTreeBuilder.java │ ├── TreeComparator.java │ ├── TreeDifference.java │ ├── TreeNode.java │ └── TreeUtils.java └── services │ ├── JarProcessor.java │ ├── MakeDiffFileTree.java │ └── TextSetter.java └── gui ├── JarsChoosingDialog.form ├── JarsChoosingDialog.java ├── MainFrame.form └── MainFrame.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/jpa-buddy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 127 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/demodiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/demodiff.png -------------------------------------------------------------------------------- /assets/image-20230919170844682.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/image-20230919170844682.png -------------------------------------------------------------------------------- /assets/image-20230919171001889.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/image-20230919171001889.png -------------------------------------------------------------------------------- /assets/image-20230919171727105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/image-20230919171727105.png -------------------------------------------------------------------------------- /assets/image-20230926115942438.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/image-20230926115942438.png -------------------------------------------------------------------------------- /assets/image-20230926120322733.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KingBridgeSS/jdifferer/7248742a33f63660e45881f8aed4c75037b03644/assets/image-20230926120322733.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.bridge 8 | jdifferer 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-assembly-plugin 15 | 3.3.0 16 | 17 | 18 | 19 | org.bridge.Main 20 | 21 | 22 | 23 | jar-with-dependencies 24 | 25 | 26 | 27 | 28 | package 29 | 30 | single 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 8 39 | 8 40 | UTF-8 41 | 42 | 43 | 44 | JCenter 45 | JCenter 46 | https://jcenter.bintray.com/ 47 | 48 | 49 | 50 | 51 | 52 | org.jd 53 | jd-core 54 | 1.1.3 55 | 56 | 57 | 58 | io.github.java-diff-utils 59 | java-diff-utils 60 | 4.12 61 | 62 | 63 | 64 | net.lingala.zip4j 65 | zip4j 66 | 2.11.5 67 | 68 | 69 | 70 | com.fifesoft 71 | rsyntaxtextarea 72 | 3.3.4 73 | 74 | 75 | 76 | commons-io 77 | commons-io 78 | 2.13.0 79 | 80 | 81 | 82 | com.intellij 83 | forms_rt 84 | 7.0.3 85 | 86 | 87 | -------------------------------------------------------------------------------- /readme-zh.md: -------------------------------------------------------------------------------- 1 | # Jdifferer 2 | 3 | [English](readme.md) | [中文](readme-zh.md) 4 | 5 | Jdifferer是一个用来比较两个java jar文件的GUI应用,方便开发者和安全研究人员快速找到两个jar文件的不同。 6 | 7 | Jdifferer会自动对两个jar文件反编译,然后用友好的GUI界面显示源码层面上的不同。 8 | 9 | 反编译的API由[jd-core](https://github.com/java-decompiler/jd-core)提供。 10 | 11 | ![image-20230926115942438](./assets/image-20230926115942438.png) 12 | 13 | 14 | ## Usage 15 | 16 | 点击左上角的Project -> open来选择需要比较的两个jar文件 17 | 18 | 在左边的侧边栏中提供三个子结点,分别代表两个jar文件的改动过的文件、增添的文件和删除的文件 19 | 20 | ![image-20230926120322733](./assets/image-20230926120322733.png) 21 | 22 | 在update节点的窗口中,红色表示删除的内容,绿色表示增添的内容,黄色表示删除的内容。(下图仅供说明) 23 | 24 | ![img](./assets/demodiff.png) 25 | 26 | ## TODO 27 | 28 | - [x] filetree 29 | - [x] main gui 30 | - [x] dialog for choosing directory 31 | - [x] code highlight 32 | - [x] diff 33 | - [x] line wrap 34 | - [ ] auto expand 35 | - [x] error dialog 36 | - [ ] ctrl f -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Jdifferer 2 | 3 | [English](readme.md) | [中文](readme-zh.md) 4 | 5 | Jdifferer is a GUI application used to diff the code of two jars. It provides a convenient approach to compare the differences of jars file for developers and security researchers. 6 | 7 | Jdifferer can decompile the jar files automatically and provides a friendly GUI view to show the differences between the files in the aspect of source code. 8 | 9 | [jd-core](https://github.com/java-decompiler/jd-core) provides the decompiling APIs. 10 | 11 | ![image-20230926115942438](./assets/image-20230926115942438.png) 12 | 13 | 14 | ## Usage 15 | 16 | Choose Project -> open on the top-left to choose the two jar files to diff. 17 | 18 | The sidebar on the left side provides three nodes, representing the updated, added and deleted files of the compared jar files, respectively. 19 | 20 | ![image-20230926120322733](./assets/image-20230926120322733.png) 21 | 22 | When you choose one update node, red contents are deleted contents, green contents are added contents and yellow contents are updated contents. (image below is yet a demo) 23 | 24 | ![img](./assets/demodiff.png) 25 | 26 | ## TODO 27 | 28 | - [x] filetree 29 | - [x] main gui 30 | - [x] dialog for choosing directory 31 | - [x] code highlight 32 | - [x] diff 33 | - [x] line wrap 34 | - [ ] auto expand 35 | - [x] error dialog 36 | - [ ] ctrl f 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/Config.java: -------------------------------------------------------------------------------- 1 | package org.bridge; 2 | 3 | public class Config { 4 | public final static String tempDirName="Jdifferer"; 5 | public final static String sourceDirName="SOURCE"; 6 | public final static String revisedDirName="REVISED"; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/Main.java: -------------------------------------------------------------------------------- 1 | package org.bridge; 2 | 3 | import org.bridge.gui.MainFrame; 4 | 5 | public class Main { 6 | private static void printBanner() { 7 | String bannerString = "\n" + 8 | " ██╗██████╗ ██╗███████╗███████╗███████╗██████╗ ███████╗██████╗ \n" + 9 | " ██║██╔══██╗██║██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝██╔══██╗\n" + 10 | " ██║██║ ██║██║█████╗ █████╗ █████╗ ██████╔╝█████╗ ██████╔╝\n" + 11 | "██ ██║██║ ██║██║██╔══╝ ██╔══╝ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██╗\n" + 12 | "╚█████╔╝██████╔╝██║██║ ██║ ███████╗██║ ██║███████╗██║ ██║\n" + 13 | " ╚════╝ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝\n" + 14 | " \n" + 15 | "--- Powered By jd-core\n"; 16 | System.out.println(bannerString); 17 | } 18 | 19 | public static void main(String[] args) { 20 | printBanner(); 21 | new MainFrame().showGUI(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/decompile/Decompiler.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.decompile; 2 | 3 | public interface Decompiler { 4 | String decompile(String fileName) throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/decompile/JDCoreDecompiler.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.decompile; 2 | 3 | import org.jd.core.v1.ClassFileToJavaSourceDecompiler; 4 | import org.jd.core.v1.api.loader.Loader; 5 | import org.jd.core.v1.api.loader.LoaderException; 6 | import org.jd.core.v1.api.printer.Printer; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Paths; 11 | 12 | public class JDCoreDecompiler implements Decompiler { 13 | @Override 14 | public String decompile(String fileName) { 15 | FileNameLoader loader = new FileNameLoader(); 16 | DefaultPrinter printer = new DefaultPrinter(); 17 | ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler(); 18 | try { 19 | decompiler.decompile(loader, printer, fileName); 20 | } catch (Exception ex) { 21 | ex.printStackTrace(); 22 | } 23 | return printer.toString(); 24 | } 25 | 26 | private class FileNameLoader implements Loader { 27 | @Override 28 | public byte[] load(String fileName) throws LoaderException { 29 | byte[] classBytes; 30 | try { 31 | classBytes = Files.readAllBytes(Paths.get(fileName)); 32 | } catch (IOException e) { 33 | throw new RuntimeException(e); 34 | } 35 | return classBytes; 36 | } 37 | 38 | @Override 39 | public boolean canLoad(String internalName) { 40 | return Files.isRegularFile(Paths.get(internalName)); 41 | } 42 | } 43 | 44 | private class DefaultPrinter implements Printer { 45 | protected static final String TAB = " "; 46 | protected static final String NEWLINE = "\n"; 47 | 48 | protected int indentationCount = 0; 49 | protected StringBuilder sb = new StringBuilder(); 50 | 51 | @Override 52 | public String toString() { 53 | return sb.toString(); 54 | } 55 | 56 | @Override 57 | public void start(int maxLineNumber, int majorVersion, int minorVersion) { 58 | } 59 | 60 | @Override 61 | public void end() { 62 | } 63 | 64 | @Override 65 | public void printText(String text) { 66 | sb.append(text); 67 | } 68 | 69 | @Override 70 | public void printNumericConstant(String constant) { 71 | sb.append(constant); 72 | } 73 | 74 | @Override 75 | public void printStringConstant(String constant, String ownerInternalName) { 76 | sb.append(constant); 77 | } 78 | 79 | @Override 80 | public void printKeyword(String keyword) { 81 | sb.append(keyword); 82 | } 83 | 84 | @Override 85 | public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { 86 | sb.append(name); 87 | } 88 | 89 | @Override 90 | public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { 91 | sb.append(name); 92 | } 93 | 94 | @Override 95 | public void indent() { 96 | this.indentationCount++; 97 | } 98 | 99 | @Override 100 | public void unindent() { 101 | this.indentationCount--; 102 | } 103 | 104 | @Override 105 | public void startLine(int lineNumber) { 106 | for (int i = 0; i < indentationCount; i++) sb.append(TAB); 107 | } 108 | 109 | @Override 110 | public void endLine() { 111 | sb.append(NEWLINE); 112 | } 113 | 114 | @Override 115 | public void extraLine(int count) { 116 | while (count-- > 0) sb.append(NEWLINE); 117 | } 118 | 119 | @Override 120 | public void startMarker(int type) { 121 | } 122 | 123 | @Override 124 | public void endMarker(int type) { 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/differ/DiffFileNodeInfo.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.differ; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.Arrays; 8 | 9 | public class DiffFileNodeInfo { 10 | private String path; 11 | private String filePath; 12 | private String directory; 13 | public DiffFileNodeInfo(String path, String directory) { 14 | this.path = path; 15 | this.directory = directory; 16 | this.filePath = String.valueOf(Paths.get(directory, path)); 17 | } 18 | 19 | public String getFilePath() { 20 | return filePath; 21 | } 22 | 23 | public Boolean fileContentsEquals(DiffFileNodeInfo info) { 24 | try { 25 | byte[] myFileContent = Files.readAllBytes(Paths.get(filePath)); 26 | byte[] anotherFileContent = Files.readAllBytes(Paths.get(info.getFilePath())); 27 | return Arrays.equals(myFileContent, anotherFileContent); 28 | } catch (IOException e) { 29 | return false; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/DeltaCommons.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | import java.util.List; 4 | 5 | public class DeltaCommons { 6 | private List differences; 7 | private TreeNode intersectionTree; 8 | 9 | public DeltaCommons(List differences, TreeNode intersectionTree) { 10 | this.differences = differences; 11 | this.intersectionTree = intersectionTree; 12 | } 13 | 14 | public List getDifferences() { 15 | return differences; 16 | } 17 | 18 | public TreeNode getIntersectionTree() { 19 | return intersectionTree; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/FileTreeBuilder.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | 6 | 7 | public class FileTreeBuilder { 8 | 9 | public static TreeNode buildFileTree(String directoryPath) { 10 | File rootDirectory = new File(directoryPath); 11 | if (!rootDirectory.exists() || !rootDirectory.isDirectory()) { 12 | return null; 13 | } 14 | 15 | return buildFileTreeHelper(rootDirectory, "",true); 16 | } 17 | 18 | private static TreeNode buildFileTreeHelper(File directory, String path, Boolean root) { 19 | String appendPath = root ? path + "/" : path + "/" + directory.getName(); 20 | TreeNode node = new TreeNode(directory.getName(), appendPath, directory.getPath(), false); 21 | 22 | File[] files = directory.listFiles(); 23 | if (files != null) { 24 | for (File file : files) { 25 | if (file.isDirectory()) { 26 | TreeNode childNode = buildFileTreeHelper(file, node.getPath(),false); 27 | if (childNode != null) { 28 | node.addChild(childNode); 29 | } 30 | } else { 31 | node.addChild(new TreeNode(file.getName(), appendPath + "/" + file.getName(), file.getPath(), true)); 32 | } 33 | } 34 | } 35 | 36 | return node; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/TreeComparator.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | 7 | public class TreeComparator { 8 | public static DeltaCommons compareTrees(TreeNode tree1, TreeNode tree2) { 9 | List differences = new ArrayList<>(); 10 | TreeNode intersectionTree = new TreeNode("", "", null, false); 11 | compareNodes(tree1, tree2, differences, intersectionTree); 12 | return new DeltaCommons(differences, intersectionTree); 13 | } 14 | 15 | private static void compareNodes(TreeNode node1, TreeNode node2, List differences, TreeNode intersectionTree) { 16 | if (node1 == null && node2 == null) { 17 | return; 18 | } 19 | if (node1 == null || node2 == null) {// current node only in A or only in B 20 | differences.add(new TreeDifference(node1, node2)); 21 | return; 22 | } 23 | for (TreeNode childA : node1.getChildren()) { 24 | boolean foundMatch = false; 25 | for (TreeNode childB : node2.getChildren()) { 26 | if (childA.getName().equals(childB.getName()) && 27 | (childA.isFile() == childB.isFile()) // one is a file and the other is a directory 28 | ) { 29 | foundMatch = true; 30 | if ((!childA.isFile()) && (!childB.isFile())) { // both are same directories 31 | TreeNode commonNode = new TreeNode(childA.getName(), childA.getPath(), childA.getFilePath(), false); 32 | intersectionTree.addChild(commonNode); 33 | compareNodes(childA, childB, differences, commonNode); 34 | } else { // both are files 35 | TreeNode commonNode = new TreeNode(childA.getName(), childA.getPath(), childA.getFilePath(), true); 36 | intersectionTree.addChild(commonNode); 37 | } 38 | break; 39 | } 40 | } 41 | if (!foundMatch) {// childA only in A 42 | differences.add(new TreeDifference(childA, null)); 43 | } 44 | } 45 | for (TreeNode childB : node2.getChildren()) { 46 | boolean foundMatch = false; 47 | for (TreeNode childA : node1.getChildren()) { 48 | if (childA.getName().equals(childB.getName()) && (childA.isFile() == childB.isFile())) { 49 | foundMatch = true; 50 | break; 51 | } 52 | } 53 | if (!foundMatch) {// childB only in B 54 | differences.add(new TreeDifference(null, childB)); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/TreeDifference.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | public class TreeDifference { 4 | 5 | private TreeNode node1; 6 | private TreeNode node2; 7 | 8 | public TreeDifference(TreeNode node1, TreeNode node2) { 9 | if ((node1 == null) == (node2 == null)) { 10 | throw new IllegalArgumentException("node1 and node2 should have and only have one null value."); 11 | } 12 | this.node1 = node1; 13 | this.node2 = node2; 14 | } 15 | 16 | public TreeNode getNode1() { 17 | return node1; 18 | } 19 | 20 | public TreeNode getNode2() { 21 | return node2; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class TreeNode { 7 | private String name; 8 | private List children; 9 | private String path; 10 | private Boolean isFile; 11 | private String filePath; 12 | 13 | public TreeNode(String name, String path,String filePath, Boolean isFile) { 14 | this.name = name; 15 | this.children = new ArrayList<>(); 16 | this.path = path; 17 | this.isFile = isFile; 18 | this.filePath=filePath; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public List getChildren() { 26 | return children; 27 | } 28 | 29 | public String getPath() { 30 | return path; 31 | } 32 | public Boolean isFile() { 33 | return isFile; 34 | } 35 | 36 | public String getFilePath() { 37 | return filePath; 38 | } 39 | 40 | public void addChild(TreeNode child) { 41 | children.add(child); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return name; 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/filetree/TreeUtils.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.filetree; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class TreeUtils { 7 | 8 | public static void printFileTree(TreeNode node, String indent) { 9 | System.out.println(indent + node.getName()); 10 | List children = node.getChildren(); 11 | for (int i = 0; i < children.size(); i++) { 12 | TreeNode child = children.get(i); 13 | if (i == children.size() - 1) { 14 | printFileTree(child, indent + " "); 15 | } else { 16 | printFileTree(child, indent + "│ "); 17 | } 18 | } 19 | } 20 | 21 | public static void printDifference(List differences) { 22 | for (TreeDifference diff : differences) { 23 | TreeNode node1 = diff.getNode1(); 24 | TreeNode node2 = diff.getNode2(); 25 | if (node1 == null) { 26 | System.out.println("++++++++++"); 27 | printFileTree(node2, ""); 28 | System.out.println("****************************************************"); 29 | } else { 30 | System.out.println("----------"); 31 | printFileTree(node1, ""); 32 | System.out.println("****************************************************"); 33 | } 34 | } 35 | } 36 | 37 | 38 | private static void getFilePathHelper(TreeNode node, List pathList) { 39 | for (TreeNode child : node.getChildren()) { 40 | if (child.isFile()) {// if child is a file, stop recursing and add to list 41 | pathList.add(child.getPath()); 42 | continue; 43 | } 44 | getFilePathHelper(child, pathList); 45 | } 46 | } 47 | 48 | public static List getFileNodeList(TreeNode root) { 49 | List nodeList = new ArrayList<>(); 50 | if(root.isFile()){ 51 | nodeList.add(root); 52 | } 53 | getFileNodeListHelper(root, nodeList); 54 | return nodeList; 55 | } 56 | 57 | private static void getFileNodeListHelper(TreeNode node, List nodeList) { 58 | for (TreeNode child : node.getChildren()) { 59 | if (child.isFile()) { 60 | nodeList.add(child); 61 | continue; 62 | } 63 | getFileNodeListHelper(child, nodeList); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/services/JarProcessor.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.services; 2 | 3 | import net.lingala.zip4j.exception.ZipException; 4 | import org.apache.commons.io.FileUtils; 5 | import org.bridge.Config; 6 | import org.bridge.core.decompile.Decompiler; 7 | import org.bridge.core.decompile.JDCoreDecompiler; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Comparator; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | public class JarProcessor { 20 | public static void main(String[] args) { 21 | System.out.println(System.getProperty("java.io.tmpdir")); 22 | } 23 | 24 | public static String[] process(String jar1, String jar2) { 25 | try { 26 | File jdifferer = new File(System.getProperty("java.io.tmpdir"), Config.tempDirName); 27 | if (jdifferer.exists()) { 28 | FileUtils.cleanDirectory(jdifferer); 29 | } else { 30 | jdifferer.mkdir(); 31 | } 32 | String sourceDir = Paths.get(jdifferer.getPath(), Config.sourceDirName).toString(); 33 | String revisedDir = Paths.get(jdifferer.getPath(), Config.revisedDirName).toString(); 34 | unzipAndDecompile(jar1, sourceDir, new JDCoreDecompiler()); 35 | unzipAndDecompile(jar2, revisedDir, new JDCoreDecompiler()); 36 | return new String[]{sourceDir, revisedDir}; 37 | } catch (IOException ex) { 38 | throw new RuntimeException(ex); 39 | } 40 | } 41 | 42 | private static void unzipAndDecompile(String jarPath, String destination, Decompiler decompiler) throws IOException { 43 | unzipJarToDirectory(jarPath, destination); 44 | decompileAllClassFile(destination, decompiler); 45 | } 46 | 47 | private static void unzipJarToDirectory(String jarPath, String destination) throws ZipException { 48 | new net.lingala.zip4j.ZipFile(jarPath).extractAll(destination); 49 | } 50 | 51 | private static void decompileAllClassFile(String directory, Decompiler decompiler) throws IOException { 52 | List pathList = listFiles(Paths.get(directory)); 53 | for (Path path : pathList) { 54 | File f = path.toFile(); 55 | if (f.isFile() && f.getName().endsWith(".class")) { 56 | try { 57 | String source = decompiler.decompile(f.getPath()); 58 | String javaPath = f.getPath().replace(".class", ".java"); 59 | Files.write(Paths.get(javaPath), source.getBytes()); 60 | Files.delete(Paths.get(f.getPath())); 61 | } catch (Exception e) { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | } 66 | } 67 | 68 | private static List listFiles(Path path) throws IOException { 69 | 70 | List result; 71 | try (Stream walk = Files.walk(path)) { 72 | result = walk.filter(Files::isRegularFile) 73 | .collect(Collectors.toList()); 74 | } 75 | return result; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/services/MakeDiffFileTree.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.services; 2 | 3 | import org.bridge.core.differ.DiffFileNodeInfo; 4 | import org.bridge.core.filetree.*; 5 | import javax.swing.JTree; 6 | import javax.swing.tree.DefaultMutableTreeNode; 7 | import javax.swing.tree.DefaultTreeModel; 8 | import java.util.*; 9 | 10 | public class MakeDiffFileTree { 11 | public static void setDiffTree(JTree tree, String dirA, String dirB) { 12 | TreeNode fileTreeA = FileTreeBuilder.buildFileTree(dirA); 13 | TreeNode fileTreeB = FileTreeBuilder.buildFileTree(dirB); 14 | DeltaCommons dc = TreeComparator.compareTrees(fileTreeA, fileTreeB); 15 | DefaultMutableTreeNode root = new DefaultMutableTreeNode("Diff Tree"); 16 | // update 17 | DefaultMutableTreeNode diffNode = new DefaultMutableTreeNode("UPDATE"); 18 | List commonsNodeList = TreeUtils.getFileNodeList(dc.getIntersectionTree()); 19 | List diffNodeList = new ArrayList<>(); 20 | for (TreeNode node : commonsNodeList) { 21 | DiffFileNodeInfo infoA = new DiffFileNodeInfo(node.getPath(), dirA); 22 | DiffFileNodeInfo infoB = new DiffFileNodeInfo(node.getPath(), dirB); 23 | if (!infoA.fileContentsEquals(infoB)) { 24 | diffNodeList.add(node); 25 | } 26 | } 27 | diffNodeList.forEach(node -> diffNode.add(new DefaultMutableTreeNode(node))); 28 | // add and delete 29 | DefaultMutableTreeNode addNode = new DefaultMutableTreeNode("ADD"); 30 | DefaultMutableTreeNode deleteNode = new DefaultMutableTreeNode("DELETE"); 31 | for (TreeDifference td : dc.getDifferences()) { 32 | if (td.getNode1() == null) { 33 | List nodeList = TreeUtils.getFileNodeList(td.getNode2()); 34 | nodeList.forEach(node -> addNode.add(new DefaultMutableTreeNode(node))); 35 | } else { 36 | List nodeList = TreeUtils.getFileNodeList(td.getNode1()); 37 | nodeList.forEach(node -> deleteNode.add(new DefaultMutableTreeNode(node))); 38 | } 39 | } 40 | root.add(diffNode); 41 | root.add(addNode); 42 | root.add(deleteNode); 43 | tree.setModel(new DefaultTreeModel(root)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/core/services/TextSetter.java: -------------------------------------------------------------------------------- 1 | package org.bridge.core.services; 2 | 3 | import com.github.difflib.DiffUtils; 4 | import com.github.difflib.patch.AbstractDelta; 5 | import com.github.difflib.patch.Patch; 6 | import org.bridge.core.filetree.TreeNode; 7 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 8 | import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 9 | 10 | import javax.swing.text.BadLocationException; 11 | import java.awt.*; 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.List; 17 | 18 | public class TextSetter { 19 | public final static Color CHANGE_COLOR = new Color(251, 232, 161); 20 | public final static Color DELETE_COLOR = new Color(255, 197, 197); 21 | public final static Color INSERT_COLOR = new Color(184, 234, 184); 22 | 23 | public static void readFileToTextArea(RSyntaxTextArea pane, TreeNode node) { 24 | Path filePath = Paths.get(node.getFilePath()); 25 | StringBuilder sb = new StringBuilder(); 26 | sb.append(node.getPath() + "\n"); 27 | try { 28 | sb.append(new String(Files.readAllBytes(filePath))); 29 | } catch (IOException ex) { 30 | throw new RuntimeException(ex); 31 | } 32 | if (filePath.toString().endsWith(".java")) { 33 | pane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); 34 | } else { 35 | pane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE); 36 | } 37 | pane.setText(sb.toString()); 38 | } 39 | 40 | public static void setDiffTextArea(RSyntaxTextArea pane1, RSyntaxTextArea pane2, TreeNode node, String dirA, String dirB) { 41 | try { 42 | Path path1 = Paths.get(dirA, node.getPath()); 43 | Path path2 = Paths.get(dirB, node.getPath()); 44 | pane1.setText(node.getPath() + "\n" + new String(Files.readAllBytes(path1))); 45 | pane2.setText(node.getPath() + "\n" + new String(Files.readAllBytes(path2))); 46 | pane1.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); 47 | pane2.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); 48 | 49 | Patch patch = getPatchFromFile(path1, path2); 50 | for (AbstractDelta delta : patch.getDeltas()) { 51 | switch (delta.getType()) { 52 | case CHANGE: { 53 | applyDeltaToPane(delta, pane1, pane2, CHANGE_COLOR, CHANGE_COLOR); 54 | } 55 | case DELETE: { 56 | applyDeltaToPane(delta, pane1, pane2, DELETE_COLOR, null); 57 | } 58 | case INSERT: { 59 | applyDeltaToPane(delta, pane1, pane2, null, INSERT_COLOR); 60 | } 61 | default: { 62 | break; 63 | } 64 | } 65 | } 66 | } catch (IOException | BadLocationException ex) { 67 | throw new RuntimeException(ex); 68 | } 69 | } 70 | 71 | private static Patch getPatchFromFile(Path path1, Path path2) throws IOException { 72 | List original = Files.readAllLines(path1); 73 | List revised = Files.readAllLines(path2); 74 | Patch patch = DiffUtils.diff(original, revised); 75 | return patch; 76 | } 77 | 78 | private static void applyDeltaToPane(AbstractDelta delta, RSyntaxTextArea pane1, RSyntaxTextArea pane2, Color color1, Color color2) throws BadLocationException { 79 | int sourcePos = delta.getSource().getPosition(); 80 | int sourceLen = delta.getSource().getLines().size(); 81 | int targetPos = delta.getTarget().getPosition(); 82 | int targetLen = delta.getTarget().getLines().size(); 83 | 84 | for (int i = sourcePos; i < sourcePos + sourceLen; i++) { 85 | pane1.addLineHighlight(i + 1, color1); 86 | } 87 | for (int i = targetPos; i < targetPos + targetLen; i++) { 88 | pane2.addLineHighlight(i + 1, color2); 89 | } 90 | } 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/gui/JarsChoosingDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/gui/JarsChoosingDialog.java: -------------------------------------------------------------------------------- 1 | package org.bridge.gui; 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints; 4 | import com.intellij.uiDesigner.core.GridLayoutManager; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.*; 9 | import java.io.File; 10 | import java.nio.file.Files; 11 | import java.util.*; 12 | import java.util.List; 13 | 14 | public class JarsChoosingDialog extends JDialog { 15 | private JPanel contentPane; 16 | private JButton buttonOK; 17 | private JButton buttonCancel; 18 | private JTextField dirTextField1; 19 | private JButton browseButton1; 20 | private JButton browseButton2; 21 | private JTextField dirTextField2; 22 | private JPanel topPanel; 23 | private List filesMessage; 24 | private Component parent; 25 | private MainFrame main; 26 | 27 | public JarsChoosingDialog(Component parent, MainFrame main) { 28 | super((Frame) parent); 29 | this.parent = parent; 30 | this.main = main; 31 | init(); 32 | } 33 | 34 | private void init() { 35 | setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 36 | filesMessage = new ArrayList<>(Arrays.asList(null, null)); 37 | 38 | setLocationAndSize(); 39 | setContentPane(contentPane); 40 | setModal(true); 41 | getRootPane().setDefaultButton(buttonOK); 42 | addListener(browseButton1, dirTextField1, true); 43 | addListener(browseButton2, dirTextField2, false); 44 | buttonOK.addActionListener(new ActionListener() { 45 | public void actionPerformed(ActionEvent e) { 46 | onOK(); 47 | } 48 | }); 49 | 50 | buttonCancel.addActionListener(new ActionListener() { 51 | public void actionPerformed(ActionEvent e) { 52 | onCancel(); 53 | } 54 | }); 55 | 56 | // call onCancel() when cross is clicked 57 | addWindowListener(new WindowAdapter() { 58 | public void windowClosing(WindowEvent e) { 59 | onCancel(); 60 | } 61 | }); 62 | 63 | // call onCancel() on ESCAPE 64 | contentPane.registerKeyboardAction(new ActionListener() { 65 | public void actionPerformed(ActionEvent e) { 66 | onCancel(); 67 | } 68 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 69 | } 70 | 71 | private void setLocationAndSize() { 72 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 73 | double widthRatio = 0.5; 74 | double heightRatio = 0.3; 75 | int dialogWidth = (int) (screenSize.width * widthRatio); 76 | int dialogHeight = (int) (screenSize.height * heightRatio); 77 | setSize(dialogWidth, dialogHeight); 78 | setLocationRelativeTo(parent); 79 | } 80 | 81 | private void addListener(JButton button, JTextField textField, Boolean origin) { 82 | button.addActionListener(e -> { 83 | JFileChooser chooser = new JFileChooser(); 84 | chooser.setCurrentDirectory(new File(".")); 85 | if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { 86 | File selectedFile = chooser.getSelectedFile(); 87 | textField.setText(selectedFile.getAbsolutePath()); 88 | filesMessage.set(origin ? 0 : 1, selectedFile); 89 | } 90 | }); 91 | } 92 | 93 | private void onOK() { 94 | if (filesMessage.get(0) == null || filesMessage.get(1) == null || 95 | !Files.isRegularFile(filesMessage.get(0).toPath()) || 96 | !Files.isRegularFile(filesMessage.get(1).toPath()) 97 | ) { 98 | alert("File Invalid."); 99 | 100 | } else { 101 | main.loadJars(filesMessage.get(0), filesMessage.get(1)); 102 | dispose(); 103 | } 104 | 105 | 106 | } 107 | 108 | private void alert(String msg) { 109 | JDialog alertDialog = new JDialog(this, "alert", true); 110 | JLabel messageLabel = new JLabel(msg); 111 | 112 | JPanel panel = new JPanel(); 113 | panel.add(messageLabel); 114 | alertDialog.add(panel); 115 | alertDialog.setLocationRelativeTo(this); 116 | alertDialog.pack(); 117 | alertDialog.setVisible(true); 118 | } 119 | 120 | private void onCancel() { 121 | dispose(); 122 | } 123 | 124 | { 125 | // GUI initializer generated by IntelliJ IDEA GUI Designer 126 | // >>> IMPORTANT!! <<< 127 | // DO NOT EDIT OR ADD ANY CODE HERE! 128 | $$$setupUI$$$(); 129 | } 130 | 131 | /** 132 | * Method generated by IntelliJ IDEA GUI Designer 133 | * >>> IMPORTANT!! <<< 134 | * DO NOT edit this method OR call it in your code! 135 | * 136 | * @noinspection ALL 137 | */ 138 | private void $$$setupUI$$$() { 139 | contentPane = new JPanel(); 140 | contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); 141 | topPanel = new JPanel(); 142 | topPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); 143 | contentPane.add(topPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 144 | final JPanel panel1 = new JPanel(); 145 | panel1.setLayout(new GridLayoutManager(3, 1, new Insets(0, 0, 0, 0), -1, -1)); 146 | topPanel.add(panel1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 147 | final JLabel label1 = new JLabel(); 148 | label1.setText("Origin JAR"); 149 | panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 150 | dirTextField1 = new JTextField(); 151 | dirTextField1.setEditable(false); 152 | panel1.add(dirTextField1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 153 | browseButton1 = new JButton(); 154 | browseButton1.setText("Browse..."); 155 | panel1.add(browseButton1, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 156 | final JPanel panel2 = new JPanel(); 157 | panel2.setLayout(new GridLayoutManager(3, 1, new Insets(0, 0, 0, 0), -1, -1)); 158 | topPanel.add(panel2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 159 | final JLabel label2 = new JLabel(); 160 | label2.setText("Origin JAR"); 161 | panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 162 | dirTextField2 = new JTextField(); 163 | dirTextField2.setEditable(false); 164 | panel2.add(dirTextField2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 165 | browseButton2 = new JButton(); 166 | browseButton2.setText("Browse..."); 167 | panel2.add(browseButton2, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 168 | final JPanel panel3 = new JPanel(); 169 | panel3.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 170 | contentPane.add(panel3, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); 171 | final JPanel panel4 = new JPanel(); 172 | panel4.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 173 | panel3.add(panel4, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 174 | buttonCancel = new JButton(); 175 | buttonCancel.setText("Cancel"); 176 | panel4.add(buttonCancel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 177 | buttonOK = new JButton(); 178 | buttonOK.setText("OK"); 179 | panel3.add(buttonOK, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 180 | } 181 | 182 | /** 183 | * @noinspection ALL 184 | */ 185 | public JComponent $$$getRootComponent$$$() { 186 | return contentPane; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/gui/MainFrame.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
77 | -------------------------------------------------------------------------------- /src/main/java/org/bridge/gui/MainFrame.java: -------------------------------------------------------------------------------- 1 | package org.bridge.gui; 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints; 4 | import com.intellij.uiDesigner.core.GridLayoutManager; 5 | import org.bridge.core.filetree.TreeNode; 6 | import org.bridge.core.services.JarProcessor; 7 | import org.bridge.core.services.MakeDiffFileTree; 8 | import org.bridge.core.services.TextSetter; 9 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 10 | 11 | import javax.swing.*; 12 | import javax.swing.event.TreeSelectionEvent; 13 | import javax.swing.event.TreeSelectionListener; 14 | import javax.swing.tree.DefaultMutableTreeNode; 15 | import java.awt.*; 16 | import java.io.File; 17 | 18 | public class MainFrame { 19 | private JPanel rootPanel; 20 | private JTree fileTree; 21 | private JPanel mainPanel; 22 | private JScrollPane fileTreeScroll; 23 | private RSyntaxTextArea textArea1; 24 | private RSyntaxTextArea textArea2; 25 | private JScrollPane codeScroll1; 26 | private JScrollPane codeScroll2; 27 | private JSplitPane codeSplitPane; 28 | private JSplitPane mainSplitPane; 29 | private JFrame frame; 30 | private String dirA; 31 | private String dirB; 32 | 33 | public void showGUI() { 34 | frame = new JFrame("Jdifferer"); 35 | frame.setContentPane(rootPanel); 36 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 37 | frame.setExtendedState(JFrame.MAXIMIZED_BOTH); 38 | loadOtherGUI(); 39 | setDividerLocation(); 40 | frame.setVisible(true); 41 | } 42 | 43 | void loadJars(File origin, File revised) { 44 | String[] dirInfo = JarProcessor.process(origin.getAbsolutePath(), revised.getAbsolutePath()); 45 | dirA = dirInfo[0]; 46 | dirB = dirInfo[1]; 47 | createFileTree(); 48 | } 49 | 50 | private void setDividerLocation() { 51 | mainSplitPane.setResizeWeight(0.15); 52 | codeSplitPane.setResizeWeight(0.5); 53 | } 54 | 55 | private void loadOtherGUI() { 56 | insertTextArea(); 57 | loadMenuBar(); 58 | } 59 | 60 | private void insertTextArea() { 61 | textArea1 = new RSyntaxTextArea(); 62 | textArea2 = new RSyntaxTextArea(); 63 | textArea1.setEditable(false); 64 | textArea1.setHighlightCurrentLine(false); 65 | textArea2.setEditable(false); 66 | textArea2.setHighlightCurrentLine(false); 67 | textArea1.setBackground(new Color(245, 245, 245)); 68 | textArea2.setBackground(new Color(245, 245, 245)); 69 | 70 | codeScroll1.setViewportView(textArea1); 71 | codeScroll2.setViewportView(textArea2); 72 | } 73 | 74 | private void loadMenuBar() { 75 | JMenuBar menuBar = new JMenuBar(); 76 | 77 | // Project 78 | JMenu projectMenu = new JMenu("Project"); 79 | JMenuItem openItem = new JMenuItem("open"); 80 | openItem.addActionListener(e -> { 81 | JDialog dialog = new JarsChoosingDialog(frame, this); 82 | dialog.setVisible(true); 83 | }); 84 | JMenuItem exitItem = new JMenuItem("exit"); 85 | exitItem.addActionListener(e -> { 86 | System.exit(0); 87 | }); 88 | projectMenu.add(openItem); 89 | projectMenu.add(exitItem); 90 | 91 | menuBar.add(projectMenu); 92 | 93 | frame.setJMenuBar(menuBar); 94 | } 95 | 96 | private void createFileTree() { 97 | MakeDiffFileTree.setDiffTree(fileTree, dirA, dirB); 98 | fileTree.addTreeSelectionListener(new TreeSelectionListener() { 99 | @Override 100 | public void valueChanged(TreeSelectionEvent e) { 101 | DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) fileTree.getLastSelectedPathComponent(); 102 | if (selectedNode != null && selectedNode.getUserObject() instanceof TreeNode) { 103 | DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) selectedNode.getParent(); 104 | TreeNode treeNode = (TreeNode) selectedNode.getUserObject(); 105 | if (parentNode.getUserObject().equals("ADD")) { 106 | TextSetter.readFileToTextArea(textArea2, treeNode); 107 | textArea2.select(0, 0); 108 | textArea1.setText(null); 109 | } else if (parentNode.getUserObject().equals("DELETE")) { 110 | TextSetter.readFileToTextArea(textArea1, treeNode); 111 | textArea1.select(0, 0); 112 | textArea2.setText(null); 113 | } else { 114 | TextSetter.setDiffTextArea(textArea1, textArea2, treeNode, dirA, dirB); 115 | textArea1.select(0, 0); 116 | textArea2.select(0, 0); 117 | } 118 | } 119 | } 120 | }); 121 | } 122 | 123 | { 124 | // GUI initializer generated by IntelliJ IDEA GUI Designer 125 | // >>> IMPORTANT!! <<< 126 | // DO NOT EDIT OR ADD ANY CODE HERE! 127 | $$$setupUI$$$(); 128 | } 129 | 130 | /** 131 | * Method generated by IntelliJ IDEA GUI Designer 132 | * >>> IMPORTANT!! <<< 133 | * DO NOT edit this method OR call it in your code! 134 | * 135 | * @noinspection ALL 136 | */ 137 | private void $$$setupUI$$$() { 138 | rootPanel = new JPanel(); 139 | rootPanel.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); 140 | mainPanel = new JPanel(); 141 | mainPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1)); 142 | rootPanel.add(mainPanel, new GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 143 | mainSplitPane = new JSplitPane(); 144 | mainPanel.add(mainSplitPane, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false)); 145 | fileTreeScroll = new JScrollPane(); 146 | mainSplitPane.setLeftComponent(fileTreeScroll); 147 | fileTree = new JTree(); 148 | fileTree.setEnabled(true); 149 | fileTree.setRootVisible(true); 150 | fileTree.setShowsRootHandles(true); 151 | fileTreeScroll.setViewportView(fileTree); 152 | codeSplitPane = new JSplitPane(); 153 | mainSplitPane.setRightComponent(codeSplitPane); 154 | codeScroll1 = new JScrollPane(); 155 | codeSplitPane.setLeftComponent(codeScroll1); 156 | codeScroll2 = new JScrollPane(); 157 | codeSplitPane.setRightComponent(codeScroll2); 158 | } 159 | 160 | /** 161 | * @noinspection ALL 162 | */ 163 | public JComponent $$$getRootComponent$$$() { 164 | return rootPanel; 165 | } 166 | } --------------------------------------------------------------------------------