diff --git a/.gitignore b/.gitignore index d5656400..ad307054 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ /target /lib /resources + +# IntelliJ +.idea/ +*.iml diff --git a/license.txt b/LICENSE similarity index 99% rename from license.txt rename to LICENSE index a8fb69f3..6a32be67 100644 --- a/license.txt +++ b/LICENSE @@ -1,32 +1,32 @@ -Luyten -Copyright (c) 2019, Deathmarine - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -1. Definitions. -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file +Luyten +Copyright (c) 2022, Deathmarine + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/ReadMe.md b/README.md similarity index 58% rename from ReadMe.md rename to README.md index 11f299c3..5acd8759 100644 --- a/ReadMe.md +++ b/README.md @@ -5,34 +5,40 @@ Java Decompiler Gui for Procyon Apache License, Version 2.0 [![Releases](https://img.shields.io/github/downloads/deathmarine/luyten/total.svg)](https://github.com/deathmarine/Luyten/releases) + ## Compilation + ***** We use maven to handle our dependencies. -* Install [Maven 3](https://maven.apache.org/download.html) +* Install [Maven 3](https://maven.apache.org/download.cgi) * Clone this repo and: `mvn clean install` ## Screenshot + ![Screen](https://i.imgur.com/phc59W6.png) ### Downloads -[Releases](https://github.com/deathmarine/Luyten/releases/latest) + +[Releases](https://github.com/deathmarine/Luyten/releases/latest) ### Bugs/Suggestions -[Issues](https://github.com/deathmarine/Luyten/issues) +[Issues](https://github.com/deathmarine/Luyten/issues) + +## Powered by -## Powered by ***** ### Procyon -© 2015 Mike Strobel -[https://bitbucket.org/mstrobel/procyon/overview](https://bitbucket.org/mstrobel/procyon/overview) -[Apache License](https://github.com/deathmarine/Luyten/blob/master/src/distfiles/Procyon.License.txt) +© 2022 Mike Strobel +[https://github.com/mstrobel/procyon](https://github.com/mstrobel/procyon) +[Apache License](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/Procyon.License.txt) ### RSyntaxTextArea -© 2012 Robert Futrell + +© 2022 Robert Futrell [https://bobbylight.github.io/RSyntaxTextArea/](https://bobbylight.github.io/RSyntaxTextArea/) -[All Rights Reserved](https://github.com/deathmarine/Luyten/blob/master/src/distfiles/RSyntaxTextArea.License.txt) +[All Rights Reserved](https://github.com/deathmarine/Luyten/blob/fixes/src/main/resources/distfiles/RSyntaxTextArea.License.txt) diff --git a/pom.xml b/pom.xml index 25b2f9f3..f7c966d9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,195 +1,154 @@ - - 4.0.0 - us.deathmarine - luyten - 0.7.0 - - - com.fifesoft - rsyntaxtextarea - 3.0.2 - - - com.apple - AppleJavaExtensions - 1.4 - - - org.bitbucket.mstrobel - procyon-core - 0.5.36 - - - org.bitbucket.mstrobel - procyon-expressions - 0.5.36 - - - org.bitbucket.mstrobel - procyon-reflection - 0.5.36 - - - org.bitbucket.mstrobel - procyon-compilertools - 0.5.36 - - - - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - src - ${project.artifactId}-${project.version}-lib - - - src - - **/*.java - - - - target - - **/*.* - - - - - - maven-compiler-plugin - 3.1 - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-shade-plugin - 2.4.2 - - - package - - shade - - - ${project.artifactId}-${project.version} - - - ${project.groupId}.${project.artifactId}.Luyten - - - - - - - - - com.akathist.maven.plugins.launch4j - launch4j-maven-plugin - 1.7.4 - - - l4j-gui - package - - launch4j - - - gui - target/${project.artifactId}-${project.version}.exe - target/${project.artifactId}-${project.version}.jar - App Err - - ${project.groupId}.${project.artifactId}.Luyten - - luyten.ico - - 1.7.0 - 1.8.0 - 128 - 1024 - - - 0.${project.version} - 0.${project.version} - Java Decompiler - 2015 - 0.${project.version} - 0.${project.version} - ${project.artifactId} - ${project.artifactId} - ${project.artifactId}-${project.version}.exe - - - - - + + 4.0.0 + + us.deathmarine + luyten + 0.7.0 + + + + 1.8 + ${java.version} + ${java.version} + UTF-8 + + + 1.4 + 0.5.36 + 3.1.5 + + + + + com.fifesoft + rsyntaxtextarea + ${rsyntaxtextarea.version} + + + com.apple + AppleJavaExtensions + ${applejavaext.version} + + + org.bitbucket.mstrobel + procyon-core + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-expressions + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-reflection + ${procyon.version} + + + org.bitbucket.mstrobel + procyon-compilertools + ${procyon.version} + + + + + ${project.artifactId}-${project.version}-lib + + + maven-compiler-plugin + 3.8.1 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + ${project.artifactId}-${project.version} + + + *:* + + module-info.class + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*LICENSE* + META-INF/*NOTICE* + META-INF/MANIFEST.MF + + + + + + ${project.groupId}.${project.artifactId}.Luyten + + ${project.version} + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + + com.akathist.maven.plugins.launch4j + launch4j-maven-plugin + 2.1.2 + + + l4j-gui + package + + launch4j + + + gui + target/${project.artifactId}-${project.version}.exe + target/${project.artifactId}-${project.version}.jar + App Err + + ${project.groupId}.${project.artifactId}.Luyten + + luyten.ico + + 1.8.0 + 128 + 1024 + + + 0.${project.version} + 0.${project.version} + Java Decompiler + 2022 + 0.${project.version} + 0.${project.version} + ${project.artifactId} + ${project.artifactId} + ${project.artifactId}-${project.version}.exe + + + + + com.googlecode.maven-download-plugin download-maven-plugin - 1.3.0 + 1.6.7 process-resources @@ -197,121 +156,113 @@ wget - https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub + + https://raw.githubusercontent.com/tofi86/universalJavaApplicationStub/master/src/universalJavaApplicationStub + ${project.build.directory}/resources universalJavaApplicationStub.sh - + - + - maven-antrun-plugin - 1.7 - - - jarbundler-gui - package - - run - - - - - - - - - - - - - + maven-antrun-plugin + 3.0.0 + + + jarbundler-gui + package + + run + + + + + + + + + + + + + - - + + - - - - - - - com.ultramixer.jarbundler - jarbundler-core - 3.3.0 - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-install-plugin - - - [2.4,) - - - install-file - - - - - - - - - - com.googlecode.maven-download-plugin - - - download-maven-plugin - - - [1.3.0,) - - - wget - - - - - - - - - - - - - - + + + + + + + com.ultramixer.jarbundler + jarbundler-core + 3.3.0 + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-install-plugin + [2.5.2,) + + install-file + + + + + + + + + com.googlecode.maven-download-plugin + download-maven-plugin + [1.6.7,) + + wget + + + + + + + + + + + + + + diff --git a/src/main/java/us/deathmarine/luyten/CellRenderer.java b/src/main/java/us/deathmarine/luyten/CellRenderer.java new file mode 100644 index 00000000..d2ab0a34 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/CellRenderer.java @@ -0,0 +1,60 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.awt.Toolkit; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; + +public class CellRenderer extends DefaultTreeCellRenderer { + + private static final long serialVersionUID = -5691181006363313993L; + + Icon pack; + Icon java_image; + Icon yml_image; + Icon file_image; + + public CellRenderer() { + this.pack = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/package_obj.png"))); + this.java_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/java.png"))); + this.yml_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/yml.png"))); + this.file_image = new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/file.png"))); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, + int row, boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; + if (node.getChildCount() > 0) { + setIcon(this.pack); + } else { + switch (FileUtil.getLanguage(getFileName(node))) { + case SyntaxConstants.SYNTAX_STYLE_JAVA: + setIcon(this.java_image); + break; + case SyntaxConstants.SYNTAX_STYLE_YAML: + setIcon(this.yml_image); + break; + default: + setIcon(this.file_image); + break; + } + } + + return this; + } + + public String getFileName(DefaultMutableTreeNode node) { + return ((TreeNodeUserObject) node.getUserObject()).getOriginalName(); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Closer.java b/src/main/java/us/deathmarine/luyten/Closer.java new file mode 100644 index 00000000..b4a489e6 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Closer.java @@ -0,0 +1,24 @@ +package us.deathmarine.luyten; + +public final class Closer { + + public static void tryClose(final AutoCloseable c) { + if (c == null) { + return; + } + try { + c.close(); + } catch (Throwable ignored) { + } + } + + public static void tryClose(final AutoCloseable... items) { + if (items == null) { + return; + } + for (AutoCloseable c : items) { + tryClose(c); + } + } + +} diff --git a/src/main/java/us/deathmarine/luyten/ConfigSaver.java b/src/main/java/us/deathmarine/luyten/ConfigSaver.java new file mode 100644 index 00000000..e21c28c0 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/ConfigSaver.java @@ -0,0 +1,224 @@ +package us.deathmarine.luyten; + +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.languages.Language; +import com.strobel.decompiler.languages.Languages; +import com.strobel.decompiler.languages.java.JavaFormattingOptions; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.prefs.Preferences; + +public class ConfigSaver { + + private static final String FLATTEN_SWITCH_BLOCKS_ID = "flattenSwitchBlocks"; + private static final String FORCE_EXPLICIT_IMPORTS_ID = "forceExplicitImports"; + private static final String SHOW_SYNTHETIC_MEMBERS_ID = "showSyntheticMembers"; + private static final String EXCLUDE_NESTED_TYPES_ID = "excludeNestedTypes"; + private static final String FORCE_EXPLICIT_TYPE_ARGUMENTS_ID = "forceExplicitTypeArguments"; + private static final String RETAIN_REDUNDANT_CASTS_ID = "retainRedundantCasts"; + private static final String INCLUDE_ERROR_DIAGNOSTICS_ID = "includeErrorDiagnostics"; + private static final String UNICODE_REPLACE_ENABLED_ID = "unicodeReplaceEnabled"; + private static final String LANGUAGE_NAME_ID = "languageName"; + + private static final String MAIN_WINDOW_ID_PREFIX = "main"; + private static final String FIND_WINDOW_ID_PREFIX = "find"; + private static final String WINDOW_IS_FULL_SCREEN_ID = "WindowIsFullScreen"; + private static final String WINDOW_WIDTH_ID = "WindowWidth"; + private static final String WINDOW_HEIGHT_ID = "WindowHeight"; + private static final String WINDOW_X_ID = "WindowX"; + private static final String WINDOW_Y_ID = "WindowY"; + + private DecompilerSettings decompilerSettings; + private WindowPosition mainWindowPosition; + private WindowPosition findWindowPosition; + private LuytenPreferences luytenPreferences; + + private static volatile ConfigSaver theLoadedInstance; + + /** + * Do not instantiate, get the loaded instance + */ + private ConfigSaver() { + } + + public static ConfigSaver getLoadedInstance() { + if (theLoadedInstance == null) { + synchronized (ConfigSaver.class) { + if (theLoadedInstance == null) { + theLoadedInstance = new ConfigSaver(); + theLoadedInstance.loadConfig(); + } + } + } + return theLoadedInstance; + } + + /** + * Do not load, get the loaded instance + */ + private void loadConfig() { + decompilerSettings = new DecompilerSettings(); + if (decompilerSettings.getJavaFormattingOptions() == null) { + decompilerSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); + } + luytenPreferences = new LuytenPreferences(); + mainWindowPosition = new WindowPosition(); + findWindowPosition = new WindowPosition(); + try { + Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); + if (!prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()) + .equals(decompilerSettings.getLanguage().getName())) + prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); + + decompilerSettings.setFlattenSwitchBlocks( + prefs.getBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks())); + decompilerSettings.setForceExplicitImports( + prefs.getBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports())); + decompilerSettings.setShowSyntheticMembers( + prefs.getBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers())); + decompilerSettings.setExcludeNestedTypes( + prefs.getBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes())); + decompilerSettings.setForceExplicitTypeArguments(prefs.getBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, + decompilerSettings.getForceExplicitTypeArguments())); + decompilerSettings.setRetainRedundantCasts( + prefs.getBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts())); + decompilerSettings.setIncludeErrorDiagnostics( + prefs.getBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics())); + decompilerSettings.setLanguage( + findLanguageByName(prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()))); + decompilerSettings.setUnicodeOutputEnabled(prefs.getBoolean(UNICODE_REPLACE_ENABLED_ID, false)); + + mainWindowPosition = loadWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX); + findWindowPosition = loadWindowPosition(prefs, FIND_WINDOW_ID_PREFIX); + luytenPreferences = loadLuytenPreferences(prefs); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private WindowPosition loadWindowPosition(Preferences prefs, String windowIdPrefix) { + WindowPosition windowPosition = new WindowPosition(); + windowPosition.setFullScreen(prefs.getBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, false)); + windowPosition.setWindowWidth(prefs.getInt(windowIdPrefix + WINDOW_WIDTH_ID, 0)); + windowPosition.setWindowHeight(prefs.getInt(windowIdPrefix + WINDOW_HEIGHT_ID, 0)); + windowPosition.setWindowX(prefs.getInt(windowIdPrefix + WINDOW_X_ID, 0)); + windowPosition.setWindowY(prefs.getInt(windowIdPrefix + WINDOW_Y_ID, 0)); + return windowPosition; + } + + // load preferences by their java variable names + private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Exception { + LuytenPreferences newLuytenPrefs = new LuytenPreferences(); + for (Field field : LuytenPreferences.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) + continue; + field.setAccessible(true); + String prefId = field.getName(); + Object defaultVal = field.get(newLuytenPrefs); + + if (field.getType() == String.class) { + String defaultStr = (String) (defaultVal == null ? "" : defaultVal); + field.set(newLuytenPrefs, prefs.get(prefId, defaultStr)); + + } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { + Boolean defaultBool = (Boolean) (defaultVal == null ? Boolean.FALSE : defaultVal); + field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); + + } else if (field.getType() == Integer.class || field.getType() == int.class) { + Integer defaultInt = (Integer) (defaultVal == null ? Integer.valueOf(0) : defaultVal); + field.setInt(newLuytenPrefs, prefs.getInt(prefId, defaultInt)); + } + } + return newLuytenPrefs; + } + + public void saveConfig() { + // Registry path on Windows Xp: + // HKEY_CURRENT_USER/Software/JavaSoft/Prefs/us/deathmarine/luyten + try { + Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); + + prefs.putBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks()); + prefs.putBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports()); + prefs.putBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers()); + prefs.putBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes()); + prefs.putBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, decompilerSettings.getForceExplicitTypeArguments()); + prefs.putBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts()); + prefs.putBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics()); + prefs.putBoolean(UNICODE_REPLACE_ENABLED_ID, decompilerSettings.isUnicodeOutputEnabled()); + prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); + + saveWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX, mainWindowPosition); + saveWindowPosition(prefs, FIND_WINDOW_ID_PREFIX, findWindowPosition); + saveLuytenPreferences(prefs); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void saveWindowPosition(Preferences prefs, String windowIdPrefix, WindowPosition windowPosition) { + prefs.putBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, windowPosition.isFullScreen()); + prefs.putInt(windowIdPrefix + WINDOW_WIDTH_ID, windowPosition.getWindowWidth()); + prefs.putInt(windowIdPrefix + WINDOW_HEIGHT_ID, windowPosition.getWindowHeight()); + prefs.putInt(windowIdPrefix + WINDOW_X_ID, windowPosition.getWindowX()); + prefs.putInt(windowIdPrefix + WINDOW_Y_ID, windowPosition.getWindowY()); + } + + // save preferences by their java variable names + private void saveLuytenPreferences(Preferences prefs) throws Exception { + for (Field field : LuytenPreferences.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) + continue; + field.setAccessible(true); + String prefId = field.getName(); + Object value = field.get(luytenPreferences); + + if (field.getType() == String.class) { + prefs.put(prefId, (String) (value == null ? "" : value)); + + } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { + prefs.putBoolean(prefId, (Boolean) (value == null ? Boolean.FALSE : value)); + + } else if (field.getType() == Integer.class || field.getType() == int.class) { + prefs.putInt(prefId, (Integer) (value == null ? Integer.valueOf(0) : value)); + } + } + } + + private Language findLanguageByName(String languageName) { + if (languageName != null) { + + if (languageName.equals(Languages.java().getName())) { + return Languages.java(); + } else if (languageName.equals(Languages.bytecode().getName())) { + return Languages.bytecode(); + } else if (languageName.equals(Languages.bytecodeAst().getName())) { + return Languages.bytecodeAst(); + } + + for (Language language : Languages.debug()) { + if (languageName.equals(language.getName())) { + return language; + } + } + } + return Languages.java(); + } + + public DecompilerSettings getDecompilerSettings() { + return decompilerSettings; + } + + public WindowPosition getMainWindowPosition() { + return mainWindowPosition; + } + + public WindowPosition getFindWindowPosition() { + return findWindowPosition; + } + + public LuytenPreferences getLuytenPreferences() { + return luytenPreferences; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java new file mode 100644 index 00000000..7aa0b796 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/DecompilerLinkProvider.java @@ -0,0 +1,383 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.FieldDefinition; +import com.strobel.assembler.metadata.FieldReference; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class DecompilerLinkProvider implements LinkProvider { + + private Map definitionToSelectionMap = new HashMap<>(); + private Map> referenceToSelectionsMap = new HashMap<>(); + private boolean isSelectionMapsPopulated = false; + + private MetadataSystem metadataSystem; + private DecompilerSettings settings; + private DecompilationOptions decompilationOptions; + private TypeDefinition type; + + private String currentTypeQualifiedName; + private String textContent = ""; + + @Override + public void generateContent() { + definitionToSelectionMap = new HashMap<>(); + referenceToSelectionsMap = new HashMap<>(); + currentTypeQualifiedName = type.getPackageName() + "." + type.getName(); + final StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter) { + @Override + public void writeDefinition(String text, Object definition, boolean isLocal) { + super.writeDefinition(text, definition, isLocal); + try { + if (text != null && definition != null) { + String uniqueStr = createUniqueStrForReference(definition); + if (uniqueStr != null) { + // fix link's underline length: _java.util.HashSet_ + // -> _HashSet_ + text = text.replaceAll("[^.]*\\.", ""); + int from = stringwriter.getBuffer().length() - text.length(); + int to = stringwriter.getBuffer().length(); + definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); + } + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + @Override + public void writeReference(String text, Object reference, boolean isLocal) { + super.writeReference(text, reference, isLocal); + try { + if (text != null && reference != null) { + String uniqueStr = createUniqueStrForReference(reference); + if (uniqueStr != null) { + text = text.replaceAll("[^.]*\\.", ""); + int from = stringwriter.getBuffer().length() - text.length(); + int to = stringwriter.getBuffer().length(); + if (reference instanceof FieldReference) { + // fix enum definition links (note: could not fix enum reference links) + if (((FieldReference) reference).isDefinition()) { + definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); + return; + } + } + if (referenceToSelectionsMap.containsKey(uniqueStr)) { + Set selectionsSet = referenceToSelectionsMap.get(uniqueStr); + if (selectionsSet != null) { + selectionsSet.add(new Selection(from, to)); + } + } else { + Set selectionsSet = new HashSet<>(); + selectionsSet.add(new Selection(from, to)); + referenceToSelectionsMap.put(uniqueStr, selectionsSet); + } + } + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + }; + plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); + textContent = stringwriter.toString(); + isSelectionMapsPopulated = true; + } + + private String createUniqueStrForReference(Object reference) { + String uniqueStr = null; + if (reference instanceof TypeReference) { + TypeReference type = (TypeReference) reference; + String pathAndTypeStr = getPathAndTypeStr(type); + if (pathAndTypeStr != null) { + uniqueStr = "type|" + pathAndTypeStr; + } + } else if (reference instanceof MethodReference) { + MethodReference method = (MethodReference) reference; + String pathAndTypeStr = getPathAndTypeStr(method.getDeclaringType()); + if (pathAndTypeStr != null) { + uniqueStr = "method|" + pathAndTypeStr + "|" + method.getName() + "|" + method.getErasedSignature(); + } + } else if (reference instanceof FieldReference) { + FieldReference field = (FieldReference) reference; + String pathAndTypeStr = getPathAndTypeStr(field.getDeclaringType()); + if (pathAndTypeStr != null) { + uniqueStr = "field|" + pathAndTypeStr + "|" + field.getName(); + } + } + return uniqueStr; + } + + private String getPathAndTypeStr(TypeReference typeRef) { + String name = typeRef.getName(); + String packageStr = typeRef.getPackageName(); + TypeReference mostOuterTypeRef = getMostOuterTypeRef(typeRef); + String mostOuterTypeName = mostOuterTypeRef.getName(); + if (name != null && packageStr != null && mostOuterTypeName != null && name.trim().length() > 0 + && mostOuterTypeName.trim().length() > 0) { + String pathStr = packageStr.replaceAll("\\.", "/") + "/" + mostOuterTypeName; + String typeStr = packageStr + "." + name.replace(".", "$"); + return pathStr + "|" + typeStr; + } + return null; + } + + private TypeReference getMostOuterTypeRef(TypeReference typeRef) { + int maxDecraringDepth = typeRef.getFullName().split("([.$])").length; + for (int i = 0; i < maxDecraringDepth; i++) { + TypeReference declaringTypeRef = typeRef.getDeclaringType(); + if (declaringTypeRef == null) { + break; + } else { + typeRef = declaringTypeRef; + } + } + if (typeRef.getName().contains("$")) { + return getMostOuterTypeRefBySlowLookuping(typeRef); + } + return typeRef; + } + + private TypeReference getMostOuterTypeRefBySlowLookuping(TypeReference typeRef) { + String name = typeRef.getName(); + if (name == null) + return typeRef; + String packageName = typeRef.getPackageName(); + if (packageName == null) + return typeRef; + String[] nameParts = name.split("\\$"); + StringBuilder newName = new StringBuilder(); + String sep = ""; + for (int i = 0; i < nameParts.length - 1; i++) { + newName.append(sep).append(nameParts[i]); + sep = "$"; + String newInternalName = packageName.replaceAll("\\.", "/") + "/" + newName; + TypeReference newTypeRef = metadataSystem.lookupType(newInternalName); + if (newTypeRef != null) { + TypeDefinition newTypeDef = newTypeRef.resolve(); + if (newTypeDef != null) { + return newTypeRef; + } + } + } + return typeRef; + } + + @Override + public String getTextContent() { + return textContent; + } + + @Override + public void processLinks() { + } + + @Override + public Map getDefinitionToSelectionMap() { + return definitionToSelectionMap; + } + + @Override + public Map> getReferenceToSelectionsMap() { + return referenceToSelectionsMap; + } + + @Override + public boolean isLinkNavigable(String uniqueStr) { + if (isSelectionMapsPopulated && definitionToSelectionMap.containsKey(uniqueStr)) + return true; + if (uniqueStr == null) + return false; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length < 3) + return false; + String typeStr = linkParts[2]; + if (typeStr.trim().length() <= 0) + return false; + TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); + if (typeRef == null) + return false; + TypeDefinition typeDef = typeRef.resolve(); + if (typeDef == null) + return false; + if (typeDef.isSynthetic()) + return false; + + if (isSelectionMapsPopulated) { + // current type's navigable definitions checked already, now it's erroneous + if (currentTypeQualifiedName == null || currentTypeQualifiedName.trim().length() <= 0) + return false; + if (typeStr.equals(currentTypeQualifiedName) || typeStr.startsWith(currentTypeQualifiedName + ".") + || typeStr.startsWith(currentTypeQualifiedName + "$")) + return false; + } + + // check linked field/method exists + if (uniqueStr.startsWith("method")) { + return findMethodInType(typeDef, uniqueStr) != null; + } else if (uniqueStr.startsWith("field")) { + return findFieldInType(typeDef, uniqueStr) != null; + } + return true; + } + + private MethodDefinition findMethodInType(TypeDefinition typeDef, String uniqueStr) { + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length != 5) + return null; + String methodName = linkParts[3]; + String methodErasedSignature = linkParts[4]; + if (methodName.trim().length() <= 0 || methodErasedSignature.trim().length() <= 0) + return null; + List declaredMethods = typeDef.getDeclaredMethods(); + if (declaredMethods == null) + return null; + boolean isFound; + for (MethodDefinition declaredMethod : declaredMethods) { + isFound = (declaredMethod != null && methodName.equals(declaredMethod.getName())); + isFound = (isFound && methodErasedSignature.equals(declaredMethod.getErasedSignature())); + if (isFound) { + if (declaredMethod.isSynthetic() && !settings.getShowSyntheticMembers()) { + return null; + } else { + return declaredMethod; + } + } + } + return null; + } + + private FieldDefinition findFieldInType(TypeDefinition typeDef, String uniqueStr) { + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length != 4) + return null; + String fieldName = linkParts[3]; + if (fieldName.trim().length() <= 0) + return null; + List declaredFields = typeDef.getDeclaredFields(); + if (declaredFields == null) + return null; + boolean isFound; + for (FieldDefinition declaredField : declaredFields) { + isFound = (declaredField != null && fieldName.equals(declaredField.getName())); + if (isFound) { + if (declaredField.isSynthetic()) { + return null; + } else { + return declaredField; + } + } + } + return null; + } + + @Override + public String getLinkDescription(String uniqueStr) { + String readableLink = null; + try { + if (uniqueStr == null) + return null; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length < 3) + return null; + String typeStr = linkParts[2]; + TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); + if (typeRef == null) + return null; + TypeDefinition typeDef = typeRef.resolve(); + if (typeDef == null) + return null; + + String declaredSuffix = ""; + String mostOuterTypeStr = linkParts[1].replaceAll("/", "."); + boolean isOwnFile = mostOuterTypeStr.equals(currentTypeQualifiedName); + if (!isOwnFile) { + declaredSuffix = " - Declared: " + mostOuterTypeStr; + } + + if (uniqueStr.startsWith("type")) { + String desc = typeDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + readableLink = desc; + } + } else if (uniqueStr.startsWith("method")) { + MethodDefinition methodDef = findMethodInType(typeDef, uniqueStr); + if (methodDef == null) + return null; + String desc = methodDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + + if (desc.contains("void ")) { + String constructorName = typeDef.getName(); + TypeReference declaringTypeRef = typeRef.getDeclaringType(); + if (declaringTypeRef != null) { + TypeDefinition declaringTypeDef = declaringTypeRef.resolve(); + if (declaringTypeDef != null) { + String declaringTypeName = declaringTypeDef.getName(); + if (declaringTypeName != null) { + constructorName = StringUtilities.removeLeft(constructorName, declaringTypeName); + constructorName = constructorName.replaceAll("^([.$])", ""); + } + } + } + desc = desc.replace("void ", constructorName); + readableLink = "Constructor: " + erasePackageInfoFromDesc(desc) + declaredSuffix; + } else { + readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; + } + } + } else if (uniqueStr.startsWith("field")) { + FieldDefinition fieldDef = findFieldInType(typeDef, uniqueStr); + if (fieldDef == null) + return null; + String desc = fieldDef.getBriefDescription(); + if (desc != null && desc.trim().length() > 0) { + readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; + } + + } + if (readableLink != null) { + readableLink = readableLink.replace("$", "."); + } + } catch (Exception e) { + readableLink = null; + Luyten.showExceptionDialog("Exception!", e); + } + return readableLink; + } + + private String erasePackageInfoFromDesc(String desc) { + // Use {0,1024} instead of * as it can't be used together with a lookbehind. + // If errors occur, increase this limit. + String limiters = "()<>\\[\\]?\\s,"; + desc = desc.replaceAll("(?<=[^" + limiters + "]{0,1024})([^" + limiters + "]*)\\.", ""); + return desc; + } + + public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, + DecompilationOptions decompilationOptions) { + this.metadataSystem = metadataSystem; + this.settings = settings; + this.decompilationOptions = decompilationOptions; + } + + public void setType(TypeDefinition type) { + this.type = type; + } + +} diff --git a/src/us/deathmarine/luyten/DirPreferences.java b/src/main/java/us/deathmarine/luyten/DirPreferences.java similarity index 96% rename from src/us/deathmarine/luyten/DirPreferences.java rename to src/main/java/us/deathmarine/luyten/DirPreferences.java index ea5df808..a687d85e 100644 --- a/src/us/deathmarine/luyten/DirPreferences.java +++ b/src/main/java/us/deathmarine/luyten/DirPreferences.java @@ -1,10 +1,11 @@ package us.deathmarine.luyten; -import javax.swing.*; import java.io.File; +import javax.swing.JFileChooser; class DirPreferences { - private LuytenPreferences luytenPrefs; + + private final LuytenPreferences luytenPrefs; public DirPreferences(LuytenPreferences luytenPrefs) { this.luytenPrefs = luytenPrefs; @@ -59,4 +60,5 @@ void saveSaveDialogDir(JFileChooser fc) { Luyten.showExceptionDialog("Exception!", e); } } -} \ No newline at end of file + +} diff --git a/src/main/java/us/deathmarine/luyten/DropListener.java b/src/main/java/us/deathmarine/luyten/DropListener.java new file mode 100644 index 00000000..17c2a574 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/DropListener.java @@ -0,0 +1,104 @@ +package us.deathmarine.luyten; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.Reader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Drag-Drop (only MainWindow should be called from here) + */ +public class DropListener implements DropTargetListener { + + private final MainWindow mainWindow; + + public DropListener(MainWindow mainWindow) { + this.mainWindow = mainWindow; + } + + @SuppressWarnings("unchecked") + @Override + public void drop(DropTargetDropEvent event) { + event.acceptDrop(DnDConstants.ACTION_COPY); + Transferable transferable = event.getTransferable(); + if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + for (DataFlavor flavor : flavors) { + try { + if (flavor.isFlavorJavaFileListType()) { + List files = (List) transferable.getTransferData(flavor); + mainWindow.onFilesDropped(files); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + event.dropComplete(true); + } else { + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + boolean handled = false; + for (DataFlavor flavor : flavors) { + if (flavor.isRepresentationClassReader()) { + try { + Reader reader = flavor.getReaderForText(transferable); + BufferedReader br = new BufferedReader(reader); + List list = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + try { + if (("" + (char) 0).equals(line)) + continue; + File file = new File(new URI(line)); + list.add(file); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + if (list.size() > 1) { + event.rejectDrop(); + return; + } + if (list.size() == 1) { + mainWindow.onFileDropped(list.get(0)); + } + event.getDropTargetContext().dropComplete(true); + handled = true; + } catch (Exception e) { + e.printStackTrace(); + } + break; + } + } + if (!handled) { + event.rejectDrop(); + } + } + + } + + @Override + public void dragEnter(DropTargetDragEvent arg0) { + } + + @Override + public void dragExit(DropTargetEvent arg0) { + } + + @Override + public void dragOver(DropTargetDragEvent arg0) { + } + + @Override + public void dropActionChanged(DropTargetDragEvent arg0) { + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FileDialog.java b/src/main/java/us/deathmarine/luyten/FileDialog.java new file mode 100644 index 00000000..63df69b4 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileDialog.java @@ -0,0 +1,136 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.io.File; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +/** + * FileChoosers for Open and Save + */ +public class FileDialog { + + private final DirPreferences dirPreferences; + private final ConfigSaver configSaver; + private final Component parent; + private JFileChooser fcOpen; + private JFileChooser fcSave; + private JFileChooser fcSaveAll; + + public FileDialog(Component parent) { + this.parent = parent; + configSaver = ConfigSaver.getLoadedInstance(); + LuytenPreferences luytenPrefs = configSaver.getLuytenPreferences(); + dirPreferences = new DirPreferences(luytenPrefs); + + new Thread(() -> { + try { + initOpenDialog(); + Thread.sleep(500); + initSaveAllDialog(); + Thread.sleep(500); + initSaveDialog(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); + } + + public File doOpenDialog() { + File selectedFile = null; + initOpenDialog(); + + dirPreferences.retrieveOpenDialogDir(fcOpen); + int returnVal = fcOpen.showOpenDialog(parent); + dirPreferences.saveOpenDialogDir(fcOpen); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcOpen.getSelectedFile(); + } + return selectedFile; + } + + public File doSaveDialog(String recommendedFileName) { + File selectedFile = null; + initSaveDialog(); + + dirPreferences.retrieveSaveDialogDir(fcSave); + fcSave.setSelectedFile(new File(recommendedFileName)); + int returnVal = fcSave.showSaveDialog(parent); + dirPreferences.saveSaveDialogDir(fcSave); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcSave.getSelectedFile(); + } + return selectedFile; + } + + public File doSaveAllDialog(String recommendedFileName) { + File selectedFile = null; + initSaveAllDialog(); + + dirPreferences.retrieveSaveDialogDir(fcSaveAll); + fcSaveAll.setSelectedFile(new File(recommendedFileName)); + int returnVal = fcSaveAll.showSaveDialog(parent); + dirPreferences.saveSaveDialogDir(fcSaveAll); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + selectedFile = fcSaveAll.getSelectedFile(); + } + return selectedFile; + } + + public synchronized void initOpenDialog() { + if (fcOpen == null) { + fcOpen = createFileChooser("*.jar", "*.zip", "*.class"); + dirPreferences.retrieveOpenDialogDir(fcOpen); + } + } + + public synchronized void initSaveDialog() { + if (fcSave == null) { + fcSave = createFileChooser("*.txt", "*.java"); + dirPreferences.retrieveSaveDialogDir(fcSave); + } + } + + public synchronized void initSaveAllDialog() { + if (fcSaveAll == null) { + fcSaveAll = createFileChooser("*.jar", "*.zip"); + dirPreferences.retrieveSaveDialogDir(fcSaveAll); + } + } + + private JFileChooser createFileChooser(String... fileFilters) { + JFileChooser fc = new JFileChooser(); + for (String fileFilter : fileFilters) { + fc.addChoosableFileFilter(new FileChooserFileFilter(fileFilter)); + } + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + return fc; + } + + public static class FileChooserFileFilter extends FileFilter { + + String objType; + + public FileChooserFileFilter(String string) { + objType = string; + } + + @Override + public boolean accept(File f) { + if (f.isDirectory()) + return true; + return f.getName().toLowerCase().endsWith(objType.substring(1)); + } + + @Override + public String getDescription() { + return objType; + } + + } + +} diff --git a/src/us/deathmarine/luyten/FileEntryNotFoundException.java b/src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java similarity index 56% rename from src/us/deathmarine/luyten/FileEntryNotFoundException.java rename to src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java index b625b589..697431a2 100644 --- a/src/us/deathmarine/luyten/FileEntryNotFoundException.java +++ b/src/main/java/us/deathmarine/luyten/FileEntryNotFoundException.java @@ -1,6 +1,7 @@ package us.deathmarine.luyten; public class FileEntryNotFoundException extends Exception { - private static final long serialVersionUID = -1019729947179642460L; + + private static final long serialVersionUID = -1019729947179642460L; } diff --git a/src/us/deathmarine/luyten/FileIsBinaryException.java b/src/main/java/us/deathmarine/luyten/FileIsBinaryException.java similarity index 55% rename from src/us/deathmarine/luyten/FileIsBinaryException.java rename to src/main/java/us/deathmarine/luyten/FileIsBinaryException.java index 19b6c4ed..f645870f 100644 --- a/src/us/deathmarine/luyten/FileIsBinaryException.java +++ b/src/main/java/us/deathmarine/luyten/FileIsBinaryException.java @@ -1,6 +1,7 @@ package us.deathmarine.luyten; public class FileIsBinaryException extends Exception { - private static final long serialVersionUID = -1497200371383232314L; + + private static final long serialVersionUID = -1497200371383232314L; } diff --git a/src/main/java/us/deathmarine/luyten/FileSaver.java b/src/main/java/us/deathmarine/luyten/FileSaver.java new file mode 100644 index 00000000..89359b13 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileSaver.java @@ -0,0 +1,322 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.ITypeLoader; +import com.strobel.assembler.metadata.JarTypeLoader; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.java.JavaFormattingOptions; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipException; +import java.util.zip.ZipOutputStream; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; + +/** + * Performs Save and Save All + */ +public class FileSaver { + + private final JProgressBar bar; + private final JLabel label; + private boolean cancel; + private boolean extracting; + + public FileSaver(JProgressBar bar, JLabel label) { + this.bar = bar; + this.label = label; + final JPopupMenu menu = new JPopupMenu("Cancel"); + final JMenuItem item = new JMenuItem("Cancel"); + item.addActionListener(arg0 -> setCancel(true)); + menu.add(item); + this.label.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent ev) { + if (SwingUtilities.isRightMouseButton(ev) && isExtracting()) + menu.show(ev.getComponent(), ev.getX(), ev.getY()); + } + }); + } + + public void saveText(final String text, final File file) { + new Thread(() -> { + DecompilerSettings settings = cloneSettings(); + boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); + long time = System.currentTimeMillis(); + try (FileOutputStream fos = new FileOutputStream(file); + OutputStreamWriter writer = isUnicodeEnabled + ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) + : new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(writer)) { + label.setText("Extracting: " + file.getName()); + bar.setVisible(true); + bw.write(text); + bw.flush(); + label.setText("Completed: " + getTime(time)); + } catch (Exception e1) { + label.setText("Cannot save file: " + file.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); + } + + public void saveAllDecompiled(final File inFile, final File outFile) { + new Thread(() -> { + long time = System.currentTimeMillis(); + try { + bar.setVisible(true); + setExtracting(true); + label.setText("Extracting: " + outFile.getName()); + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + String inFileName = inFile.getName().toLowerCase(); + + if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { + doSaveJarDecompiled(inFile, outFile); + } else if (inFileName.endsWith(".class")) { + doSaveClassDecompiled(inFile, outFile); + } else { + doSaveUnknownFile(inFile, outFile); + } + if (cancel) { + label.setText("Cancelled"); + outFile.delete(); + setCancel(false); + } else { + label.setText("Completed: " + getTime(time)); + } + } catch (Exception e1) { + label.setText("Cannot save file: " + outFile.getName()); + Luyten.showExceptionDialog("Unable to save file!\n", e1); + } finally { + setExtracting(false); + bar.setVisible(false); + } + }).start(); + } + + private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { + try (JarFile jfile = new JarFile(inFile); + FileOutputStream dest = new FileOutputStream(outFile); + BufferedOutputStream buffDest = new BufferedOutputStream(dest); + ZipOutputStream out = new ZipOutputStream(buffDest)) { + bar.setMinimum(0); + bar.setMaximum(jfile.size()); + byte[] data = new byte[1024]; + DecompilerSettings settings = cloneSettings(); + LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + ITypeLoader jarLoader = new JarTypeLoader(jfile); + typeLoader.getTypeLoaders().add(jarLoader); + + DecompilationOptions decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + + List mass; + JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); + LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); + if (luytenPrefs.isFilterOutInnerClassEntries()) { + mass = jarEntryFilter.getEntriesWithoutInnerClasses(); + } else { + mass = jarEntryFilter.getAllEntriesFromJar(); + } + + Enumeration ent = jfile.entries(); + Set history = new HashSet<>(); + int tick = 0; + while (ent.hasMoreElements() && !cancel) { + bar.setValue(++tick); + JarEntry entry = ent.nextElement(); + if (!mass.contains(entry.getName())) + continue; + label.setText("Extracting: " + entry.getName()); + bar.setVisible(true); + if (entry.getName().endsWith(".class")) { + JarEntry etn = new JarEntry(entry.getName().replace(".class", ".java")); + label.setText("Extracting: " + etn.getName()); + System.out.println("[SaveAll]: " + etn.getName() + " -> " + outFile.getName()); + + if (history.add(etn.getName())) { + out.putNextEntry(etn); + try { + boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); + String internalName = StringUtilities.removeRight(entry.getName(), ".class"); + TypeReference type = metadataSystem.lookupType(internalName); + TypeDefinition resolvedType; + if ((type == null) || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, StandardCharsets.UTF_8) + : new OutputStreamWriter(out); + PlainTextOutput plainTextOutput = new PlainTextOutput(writer); + plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + writer.flush(); + } catch (Exception e) { + label.setText("Cannot decompile file: " + entry.getName()); + Luyten.showExceptionDialog("Unable to Decompile file!\nSkipping file...", e); + } finally { + out.closeEntry(); + } + } + } else { + try { + JarEntry etn = new JarEntry(entry.getName()); + if (entry.getName().endsWith(".java")) + etn = new JarEntry(entry.getName().replace(".java", ".src.java")); + if (history.add(etn.getName())) { + out.putNextEntry(etn); + try { + InputStream in = jfile.getInputStream(etn); + if (in != null) { + try { + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + out.write(data, 0, count); + } + } finally { + in.close(); + } + } + } finally { + out.closeEntry(); + } + } + } catch (ZipException ze) { + if (!ze.getMessage().contains("duplicate")) { + throw ze; + } + } + } + } + } + } + + private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { + DecompilerSettings settings = cloneSettings(); + LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + TypeReference type = metadataSystem.lookupType(inFile.getCanonicalPath()); + + DecompilationOptions decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + + boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); + TypeDefinition resolvedType; + if (type == null || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + String decompiledSource = stringwriter.toString(); + + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + try (FileOutputStream fos = new FileOutputStream(outFile); + OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, StandardCharsets.UTF_8) + : new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(writer)) { + bw.write(decompiledSource); + bw.flush(); + } + } + + private void doSaveUnknownFile(File inFile, File outFile) throws Exception { + try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile)) { + System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); + + byte[] data = new byte[1024]; + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + out.write(data, 0, count); + } + } + } + + private DecompilerSettings cloneSettings() { + DecompilerSettings settings = ConfigSaver.getLoadedInstance().getDecompilerSettings(); + DecompilerSettings newSettings = new DecompilerSettings(); + if (newSettings.getJavaFormattingOptions() == null) { + newSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); + } + // synchronized: against main menu changes + synchronized (settings) { + newSettings.setExcludeNestedTypes(settings.getExcludeNestedTypes()); + newSettings.setFlattenSwitchBlocks(settings.getFlattenSwitchBlocks()); + newSettings.setForceExplicitImports(settings.getForceExplicitImports()); + newSettings.setForceExplicitTypeArguments(settings.getForceExplicitTypeArguments()); + newSettings.setOutputFileHeaderText(settings.getOutputFileHeaderText()); + newSettings.setLanguage(settings.getLanguage()); + newSettings.setShowSyntheticMembers(settings.getShowSyntheticMembers()); + newSettings.setAlwaysGenerateExceptionVariableForCatchBlocks( + settings.getAlwaysGenerateExceptionVariableForCatchBlocks()); + newSettings.setOutputDirectory(settings.getOutputDirectory()); + newSettings.setRetainRedundantCasts(settings.getRetainRedundantCasts()); + newSettings.setIncludeErrorDiagnostics(settings.getIncludeErrorDiagnostics()); + newSettings.setIncludeLineNumbersInBytecode(settings.getIncludeLineNumbersInBytecode()); + newSettings.setRetainPointlessSwitches(settings.getRetainPointlessSwitches()); + newSettings.setUnicodeOutputEnabled(settings.isUnicodeOutputEnabled()); + newSettings.setMergeVariables(settings.getMergeVariables()); + newSettings.setShowDebugLineNumbers(settings.getShowDebugLineNumbers()); + } + return newSettings; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public boolean isExtracting() { + return extracting; + } + + public void setExtracting(boolean extracting) { + this.extracting = extracting; + } + + public static String getTime(long time) { + long lap = System.currentTimeMillis() - time; + lap = lap / 1000; + StringBuilder sb = new StringBuilder(); + long hour = ((lap / 60) / 60); + long min = ((lap - (hour * 60 * 60)) / 60); + long sec = ((lap - (hour * 60 * 60) - (min * 60))); + if (hour > 0) + sb.append("Hour:").append(hour).append(" "); + sb.append("Min(s): ").append(min).append(" Sec: ").append(sec); + return sb.toString(); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FileUtil.java b/src/main/java/us/deathmarine/luyten/FileUtil.java new file mode 100644 index 00000000..ac12275e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FileUtil.java @@ -0,0 +1,33 @@ +package us.deathmarine.luyten; + +import java.io.File; +import org.fife.ui.rsyntaxtextarea.FileTypeUtil; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; + +public final class FileUtil { + + private static final FileTypeUtil FILE_TYPE_UTIL = FileTypeUtil.get(); + + private FileUtil() { + throw new UnsupportedOperationException(); + } + + public static String getLanguage(String fileName) { + // Hard code .class -> Java mapping + if (fileName.toLowerCase().endsWith(".class")) { + return SyntaxConstants.SYNTAX_STYLE_JAVA; + } + // Otherwise, let RSTA do the job + return FILE_TYPE_UTIL.guessContentType(new File(fileName)); + } + + public static void setLanguage(RSyntaxTextArea area, String fileName) { + String type = getLanguage(fileName); + if (type == null || type.equals(SyntaxConstants.SYNTAX_STYLE_NONE)) { + type = FILE_TYPE_UTIL.guessContentType(area); + } + area.setSyntaxEditingStyle(type); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FindAllBox.java b/src/main/java/us/deathmarine/luyten/FindAllBox.java new file mode 100644 index 00000000..8ba55251 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FindAllBox.java @@ -0,0 +1,407 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.Collections; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.DefaultListModel; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; + +/** + * this is the Find All Dialog + *

+ * Change with 1.1 + * Adjust the find all box width + *

+ * + * @author clevertension + * @version 1.1 + */ +public class FindAllBox extends JDialog { + + private static final long serialVersionUID = -4125409760166690462L; + private static final int MIN_WIDTH = 640; + private boolean searching; + + private final JButton findButton; + private final JTextField textField; + private final JCheckBox mcase; + private final JCheckBox regex; + private final JCheckBox wholew; + private final JCheckBox classname; + private final JList list; + private final JProgressBar progressBar; + boolean locked; + + private final JLabel statusLabel = new JLabel(""); + + private final DefaultListModel classesList = new DefaultListModel<>(); + + private Thread tmp_thread; + + private final MainWindow mainWindow; + + public FindAllBox(final MainWindow mainWindow) { + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + this.setHideOnEscapeButton(); + + progressBar = new JProgressBar(0, 100); + this.mainWindow = mainWindow; + + JLabel label = new JLabel("Find What:"); + textField = new JTextField(); + findButton = new JButton("Find"); + findButton.addActionListener(new FindButton()); + + mcase = new JCheckBox("Match Case"); + regex = new JCheckBox("Regex"); + wholew = new JCheckBox("Whole Words"); + classname = new JCheckBox("Classnames"); + + this.getRootPane().setDefaultButton(findButton); + + list = new JList<>(classesList); + list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + list.setLayoutOrientation(JList.VERTICAL_WRAP); + list.setVisibleRowCount(-1); + list.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + @SuppressWarnings("unchecked") + JList list = (JList) evt.getSource(); + if (evt.getClickCount() == 2) { + int index = list.locationToIndex(evt.getPoint()); + String entryName = list.getModel().getElementAt(index); + String[] array = entryName.split("/"); + if (entryName.toLowerCase().endsWith(".class")) { + String internalName = StringUtilities.removeRight(entryName, ".class"); + TypeReference type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + try { + mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], + entryName, + null); + } catch (Exception ignored) { + for (Model m : mainWindow.getModels()) { + try { + m.extractClassToTextPane(type, array[array.length - 1], entryName, null); + } catch (Exception ignored1) { + } + } + } + + } else { + try { + JarFile jfile = new JarFile(mainWindow.getSelectedModel().getOpenedFile()); + mainWindow.getSelectedModel().extractSimpleFileEntryToTextPane( + jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], + entryName); + jfile.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + } + }); + list.setLayoutOrientation(JList.VERTICAL); + JScrollPane listScroller = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int width = (int) (screenSize.width * 0.35); + if (width < MIN_WIDTH) { + width = MIN_WIDTH; + } + final Dimension center = new Dimension(width, 500); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + this.setResizable(false); + + GroupLayout layout = new GroupLayout(getRootPane()); + getRootPane().setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup( + layout.createSequentialGroup().addComponent(label) + .addGroup( + layout.createParallelGroup(Alignment.LEADING).addComponent(statusLabel) + .addComponent(textField) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(mcase)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(wholew)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(classname))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(listScroller) + .addComponent(progressBar)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton)) + + ); + + layout.linkSize(SwingConstants.HORIZONTAL, findButton); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) + .addComponent(findButton)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase).addComponent(wholew) + .addComponent(regex).addComponent(classname)) + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(listScroller)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING)).addComponent(statusLabel) + .addComponent(progressBar)); + this.adjustWindowPositionBySavedState(); + this.setSaveWindowPositionOnClosing(); + + this.setName("Find All"); + this.setTitle("Find All"); + } + + private class FindButton extends AbstractAction { + private static final long serialVersionUID = 75954129199541874L; + + @Override + public void actionPerformed(ActionEvent event) { + tmp_thread = new Thread(() -> { + if (findButton.getText().equals("Stop")) { + if (tmp_thread != null) + tmp_thread.interrupt(); + setStatus("Stopped."); + findButton.setText("Find"); + locked = false; + } else { + findButton.setText("Stop"); + classesList.clear(); + ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); + DecompilerSettings settings = configSaver.getDecompilerSettings(); + File inFile = mainWindow.getSelectedModel().getOpenedFile(); + boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() + .isFilterOutInnerClassEntries(); + try { + JarFile jfile = new JarFile(inFile); + Enumeration entLength = jfile.entries(); + initProgressBar(Collections.list(entLength).size()); + Enumeration ent = jfile.entries(); + while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { + JarEntry entry = ent.nextElement(); + String name = entry.getName(); + setStatus(name); + if (filter && name.contains("$")) + continue; + if (locked || classname.isSelected()) { + locked = true; + if (search(entry.getName())) + addClassName(entry.getName()); + } else { + if (entry.getName().endsWith(".class")) { + synchronized (settings) { + String internalName = StringUtilities.removeRight(entry.getName(), ".class"); + TypeReference type; + try { + type = mainWindow.getSelectedModel().getMetadataSystem().lookupType(internalName); + TypeDefinition resolvedType; + if (type != null && ((resolvedType = type.resolve()) != null)) { + StringWriter stringwriter = new StringWriter(); + DecompilationOptions decompilationOptions; + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled( + decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, + decompilationOptions); + if (search(stringwriter.toString())) + addClassName(entry.getName()); + } + } catch (IllegalStateException ise) { + if (ise.getMessage().contains("Invalid BootstrapMethods attribute entry: " + + "2 additional arguments required for method " + + "java/lang/invoke/StringConcatFactory.makeConcatWithConstants, " + + "but only 1 specified.")) { + // Known issue of Procyon <= 0.5.35 and fix not yet released, refer to + // https://web.archive.org/web/20200722211732/https://bitbucket.org/mstrobel/procyon/issues/336/ + // Searching in a WAR or JAR file could pop-up a lot of error dialogs + // for a lot of class files, we simply say nothing here + addClassName(entry.getName() + " (search failed due to known " + + "Exception in Procyon <= 0.5.35. Opening file will fail " + + "too)"); + } else { + // all other IllegalStateException cases + addClassName(entry.getName() + " (search failed due to Exception. " + + "Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), + ise); + } + } catch (Exception e) { + addClassName(entry.getName() + " (search failed due to Exception. " + + "Opening file will fail too)"); + Luyten.showExceptionDialog("Caught Exception on: " + entry.getName(), e); + } + } + } else { + + StringBuilder sb = new StringBuilder(); + double ascii = 0; + double other = 0; + try (InputStreamReader inputStreamReader = new InputStreamReader( + jfile.getInputStream(entry)); + BufferedReader reader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + // Source: https://stackoverflow.com/a/13533390/5894824 + for (byte b : line.getBytes()) { + if (b == 0x09 || b == 0x0A || b == 0x0C || b == 0x0D || (b >= 0x20 && b <= 0x7E)) + ascii++; + else other++; + } + } + } + + if ((other == 0 || ascii / (ascii + other) > 0.5) && search(sb.toString())) + addClassName(entry.getName()); + } + } + } + setSearching(false); + if (findButton.getText().equals("Stop")) { + setStatus("Done."); + findButton.setText("Find"); + locked = false; + } + jfile.close(); + locked = false; + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + + } + }); + tmp_thread.start(); + + } + + } + + private boolean search(String bulk) { + String a = textField.getText(); + String b = bulk; + if (regex.isSelected()) + return Pattern.matches(a, b); + if (wholew.isSelected()) + a = " " + a + " "; + if (!mcase.isSelected()) { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + return b.contains(a); + } + + private void setHideOnEscapeButton() { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = 6846566740472934801L; + + @Override + public void actionPerformed(ActionEvent e) { + FindAllBox.this.setVisible(false); + } + }; + + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); + this.getRootPane().getActionMap().put("ESCAPE", escapeAction); + } + + private void adjustWindowPositionBySavedState() { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + + if (windowPosition.isSavedWindowPositionValid()) { + this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); + } + } + + private void setSaveWindowPositionOnClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowDeactivated(WindowEvent e) { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + windowPosition.readPositionFromDialog(FindAllBox.this); + } + }); + } + + public void showFindBox() { + this.setVisible(true); + this.textField.requestFocus(); + } + + public void hideFindBox() { + this.setVisible(false); + } + + public void setStatus(String text) { + if (text.length() > 25) { + this.statusLabel.setText("Searching in file: ..." + text.substring(text.length() - 25)); + } else { + this.statusLabel.setText("Searching in file: " + text); + } + + progressBar.setValue(progressBar.getValue() + 1); + } + + public void addClassName(String className) { + this.classesList.addElement(className); + } + + public void initProgressBar(Integer length) { + progressBar.setMaximum(length); + progressBar.setValue(0); + progressBar.setStringPainted(true); + } + + public boolean isSearching() { + return searching; + } + + public void setSearching(boolean searching) { + this.searching = searching; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/FindBox.java b/src/main/java/us/deathmarine/luyten/FindBox.java new file mode 100644 index 00000000..3b316adb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/FindBox.java @@ -0,0 +1,234 @@ +package us.deathmarine.luyten; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rtextarea.SearchContext; +import org.fife.ui.rtextarea.SearchEngine; + +public class FindBox extends JDialog { + + private static final long serialVersionUID = -4125409760166690462L; + + JCheckBox mcase; + JCheckBox regex; + JCheckBox wholew; + JCheckBox reverse; + JCheckBox wrap; + private final JButton findButton; + JTextField textField; + private final MainWindow mainWindow; + + public void showFindBox() { + this.setVisible(true); + this.textField.requestFocus(); + this.textField.selectAll(); + } + + public void hideFindBox() { + this.setVisible(false); + } + + public FindBox(final MainWindow mainWindow) { + this.mainWindow = mainWindow; + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + this.setHideOnEscapeButton(); + + JLabel label = new JLabel("Find What:"); + textField = new JTextField(); + + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + textField.setText(pane.getSelectedText()); + } + mcase = new JCheckBox("Match Case"); + regex = new JCheckBox("Regex"); + wholew = new JCheckBox("Whole Words"); + reverse = new JCheckBox("Search Backwards"); + wrap = new JCheckBox("Wrap"); + + findButton = new JButton("Find"); + findButton.addActionListener(new FindButton()); + this.getRootPane().setDefaultButton(findButton); + + KeyStroke funcF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(funcF3, "FindNext"); + this.getRootPane().getActionMap().put("FindNext", new FindExploreAction(true)); + + KeyStroke sfuncF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF3, "FindPrevious"); + this.getRootPane().getActionMap().put("FindPrevious", new FindExploreAction(false)); + + mcase.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + regex.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + wholew.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + reverse.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + wrap.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + final Dimension center = new Dimension((int) (screenSize.width * 0.35), + Math.min((int) (screenSize.height * 0.20), 200)); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + this.setResizable(false); + + GroupLayout layout = new GroupLayout(getRootPane()); + getRootPane().setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(label) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(textField) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(mcase) + .addComponent(wholew).addComponent(wrap)) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex) + .addComponent(reverse)))) + .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton))); + + layout.linkSize(SwingConstants.HORIZONTAL, findButton); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) + .addComponent(findButton)) + .addGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase) + .addComponent(regex)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wholew) + .addComponent(reverse)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wrap))))); + + this.adjustWindowPositionBySavedState(); + this.setSaveWindowPositionOnClosing(); + + this.setName("Find"); + this.setTitle("Find"); + this.setVisible(true); + } + + private class FindButton extends AbstractAction { + private static final long serialVersionUID = 75954129199541874L; + + @Override + public void actionPerformed(ActionEvent event) { + if (textField.getText().length() == 0) + return; + + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + + SearchContext context = new SearchContext(); + context.setSearchFor(textField.getText()); + context.setMatchCase(mcase.isSelected()); + context.setRegularExpression(regex.isSelected()); + context.setSearchForward(!reverse.isSelected()); + context.setWholeWord(wholew.isSelected()); + + if (!SearchEngine.find(pane, context).wasFound()) { + if (wrap.isSelected()) { + pane.setSelectionStart(0); + pane.setSelectionEnd(0); + } else { + mainWindow.getLabel().setText("Search Complete"); + } + } + } + + } + + private void setHideOnEscapeButton() { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = 5572504000935312338L; + + @Override + public void actionPerformed(ActionEvent e) { + FindBox.this.setVisible(false); + } + }; + + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); + this.getRootPane().getActionMap().put("ESCAPE", escapeAction); + } + + private void adjustWindowPositionBySavedState() { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + + if (windowPosition.isSavedWindowPositionValid()) { + this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); + } + } + + private void setSaveWindowPositionOnClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowDeactivated(WindowEvent e) { + WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); + windowPosition.readPositionFromDialog(FindBox.this); + } + }); + } + + public void fireExploreAction(boolean direction) { + new FindExploreAction(direction).actionPerformed(null); + } + + class FindExploreAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = -4391670062679240573L; + boolean direction; + + public FindExploreAction(boolean forward) { + direction = forward; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (textField.getText().length() == 0) + return; + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + SearchContext context = new SearchContext(); + context.setSearchFor(textField.getText()); + context.setMatchCase(mcase.isSelected()); + context.setRegularExpression(regex.isSelected()); + context.setSearchForward(direction); + context.setWholeWord(wholew.isSelected()); + + if (!SearchEngine.find(pane, context).wasFound()) { + if (wrap.isSelected()) { + pane.setSelectionStart(0); + pane.setSelectionEnd(0); + } else { + mainWindow.getLabel().setText("Search Complete"); + } + } + + } + + } + +} diff --git a/src/main/java/us/deathmarine/luyten/JFontChooser.java b/src/main/java/us/deathmarine/luyten/JFontChooser.java new file mode 100644 index 00000000..55786131 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/JFontChooser.java @@ -0,0 +1,755 @@ +package us.deathmarine.luyten; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.DefaultListCellRenderer; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; + +/** + * The JFontChooser class is a swing component for font selection. + * This class has JFileChooser like APIs. The following code pops + * up a font chooser dialog. + **/ +public class JFontChooser extends JComponent { + + /** + * + */ + private static final long serialVersionUID = 8856126034081661L; + // class variables + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int OK_OPTION = 0; + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int CANCEL_OPTION = 1; + /** + * Return value from showDialog(). + * + * @see #showDialog + **/ + public static final int ERROR_OPTION = -1; + private static final Font DEFAULT_SELECTED_FONT = new Font("Serif", Font.PLAIN, 12); + private static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 10); + private static final int[] FONT_STYLE_CODES = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC}; + private static final String[] DEFAULT_FONT_SIZE_STRINGS = {"8", "9", "10", "11", "12", "14", "16", "18", "20", + "22", "24", "26", "28", "36", "48", "72",}; + + // instance variables + protected int dialogResultValue = ERROR_OPTION; + + protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer(); + + private String[] fontStyleNames; + private String[] fontFamilyNames; + private final String[] fontSizeStrings; + private JTextField fontFamilyTextField; + private JTextField fontStyleTextField; + private JTextField fontSizeTextField; + private JList fontNameList; + private JList fontStyleList; + private JList fontSizeList; + private JPanel fontNamePanel; + private JPanel fontStylePanel; + private JPanel fontSizePanel; + private JPanel samplePanel; + private JTextField sampleText; + + /** + * Constructs a JFontChooser object. + **/ + public JFontChooser() { + this(DEFAULT_FONT_SIZE_STRINGS); + } + + /** + * Constructs a JFontChooser object using the given font size + * array. + * + * @param fontSizeStrings the array of font size string. + **/ + public JFontChooser(String[] fontSizeStrings) { + if (fontSizeStrings == null) { + fontSizeStrings = DEFAULT_FONT_SIZE_STRINGS; + } + this.fontSizeStrings = fontSizeStrings; + + JPanel selectPanel = new JPanel(); + selectPanel.setLayout(new BoxLayout(selectPanel, BoxLayout.X_AXIS)); + selectPanel.add(getFontFamilyPanel()); + selectPanel.add(getFontStylePanel()); + selectPanel.add(getFontSizePanel()); + + JPanel contentsPanel = new JPanel(); + contentsPanel.setLayout(new GridLayout(2, 1)); + contentsPanel.add(selectPanel, BorderLayout.NORTH); + contentsPanel.add(getSamplePanel(), BorderLayout.CENTER); + + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + this.add(contentsPanel); + this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + this.setSelectedFont(DEFAULT_SELECTED_FONT); + } + + public JTextField getFontFamilyTextField() { + if (fontFamilyTextField == null) { + fontFamilyTextField = new JTextField(); + fontFamilyTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontFamilyTextField)); + fontFamilyTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontFamilyList())); + fontFamilyTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontFamilyList())); + fontFamilyTextField.setFont(DEFAULT_FONT); + + } + return fontFamilyTextField; + } + + public JTextField getFontStyleTextField() { + if (fontStyleTextField == null) { + fontStyleTextField = new JTextField(); + fontStyleTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontStyleTextField)); + fontStyleTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontStyleList())); + fontStyleTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontStyleList())); + fontStyleTextField.setFont(DEFAULT_FONT); + + } + return fontStyleTextField; + } + + public JTextField getFontSizeTextField() { + if (fontSizeTextField == null) { + fontSizeTextField = new JTextField(); + fontSizeTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontSizeTextField)); + fontSizeTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontSizeList())); + fontSizeTextField.getDocument() + .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontSizeList())); + fontSizeTextField.setFont(DEFAULT_FONT); + } + return fontSizeTextField; + } + + public JList getFontFamilyList() { + if (fontNameList == null) { + fontNameList = new JList(getFontFamilies()); + fontNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontNameList.addListSelectionListener(new ListSelectionHandler(getFontFamilyTextField())); + fontNameList.setSelectedIndex(0); + + // Draw Fonts + fontNameList.setCellRenderer(new DefaultListCellRenderer() { + /** + * + */ + private static final long serialVersionUID = -6753380853569310954L; + + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + + if (value instanceof String) { + renderer.setText((String) value); + renderer.setFont(new Font((String) value, DEFAULT_FONT.getStyle(), DEFAULT_FONT.getSize() + 2)); + } else { + renderer.setText(""); + } + return renderer; + } + }); + fontNameList.setFocusable(false); + } + return fontNameList; + } + + public JList getFontStyleList() { + if (fontStyleList == null) { + fontStyleList = new JList(getFontStyleNames()); + fontStyleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontStyleList.addListSelectionListener(new ListSelectionHandler(getFontStyleTextField())); + fontStyleList.setSelectedIndex(0); + fontStyleList.setFocusable(false); + fontStyleList.setCellRenderer(new DefaultListCellRenderer() { + /** + * + */ + private static final long serialVersionUID = -3904668242514776943L; + + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { + + JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + + if (value instanceof String) { + renderer.setText((String) value); + renderer.setFont( + new Font(DEFAULT_FONT.getName(), FONT_STYLE_CODES[index], DEFAULT_FONT.getSize() + 2)); + } else { + renderer.setText(""); + } + return renderer; + } + }); + } + return fontStyleList; + } + + public JList getFontSizeList() { + if (fontSizeList == null) { + fontSizeList = new JList(this.fontSizeStrings); + fontSizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + fontSizeList.addListSelectionListener(new ListSelectionHandler(getFontSizeTextField())); + fontSizeList.setSelectedIndex(0); + fontSizeList.setFont(DEFAULT_FONT); + fontSizeList.setFocusable(false); + } + return fontSizeList; + } + + /** + * Get the family name of the selected font. + * + * @return the font family of the selected font. + * @see #setSelectedFontFamily + **/ + public String getSelectedFontFamily() { + return (String) getFontFamilyList().getSelectedValue(); + } + + /** + * Get the style of the selected font. + * + * @return the style of the selected font. Font.PLAIN, + * Font.BOLD, Font.ITALIC, + * Font.BOLD|Font.ITALIC + * @see java.awt.Font#PLAIN + * @see java.awt.Font#BOLD + * @see java.awt.Font#ITALIC + * @see #setSelectedFontStyle + **/ + public int getSelectedFontStyle() { + int index = getFontStyleList().getSelectedIndex(); + return FONT_STYLE_CODES[index]; + } + + /** + * Get the size of the selected font. + * + * @return the size of the selected font + * @see #setSelectedFontSize + **/ + public int getSelectedFontSize() { + int fontSize; + String fontSizeString = getFontSizeTextField().getText(); + while (true) { + try { + fontSize = Integer.parseInt(fontSizeString); + break; + } catch (NumberFormatException e) { + fontSizeString = (String) getFontSizeList().getSelectedValue(); + getFontSizeTextField().setText(fontSizeString); + } + } + + return fontSize; + } + + /** + * Get the selected font. + * + * @return the selected font + * @see #setSelectedFont + * @see java.awt.Font + **/ + public Font getSelectedFont() { + return new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); + } + + /** + * Set the family name of the selected font. + * + * @param name the family name of the selected font. + * @see #getSelectedFontFamily + **/ + public void setSelectedFontFamily(String name) { + String[] names = getFontFamilies(); + for (int i = 0; i < names.length; i++) { + if (names[i].equalsIgnoreCase(name)) { + getFontFamilyList().setSelectedIndex(i); + break; + } + } + updateSampleFont(); + } + + /** + * Set the style of the selected font. + * + * @param style the size of the selected font. Font.PLAIN, + * Font.BOLD, Font.ITALIC, or + * Font.BOLD|Font.ITALIC. + * @see java.awt.Font#PLAIN + * @see java.awt.Font#BOLD + * @see java.awt.Font#ITALIC + * @see #getSelectedFontStyle + **/ + public void setSelectedFontStyle(int style) { + for (int i = 0; i < FONT_STYLE_CODES.length; i++) { + if (FONT_STYLE_CODES[i] == style) { + getFontStyleList().setSelectedIndex(i); + break; + } + } + updateSampleFont(); + } + + /** + * Set the size of the selected font. + * + * @param size the size of the selected font + * @see #getSelectedFontSize + **/ + public void setSelectedFontSize(int size) { + String sizeString = String.valueOf(size); + for (int i = 0; i < this.fontSizeStrings.length; i++) { + if (this.fontSizeStrings[i].equals(sizeString)) { + getFontSizeList().setSelectedIndex(i); + break; + } + } + getFontSizeTextField().setText(sizeString); + updateSampleFont(); + } + + /** + * Set the selected font. + * + * @param font the selected font + * @see #getSelectedFont + * @see java.awt.Font + **/ + public void setSelectedFont(Font font) { + setSelectedFontFamily(font.getFamily()); + setSelectedFontStyle(font.getStyle()); + setSelectedFontSize(font.getSize()); + } + + public String getVersionString() { + return ("Version"); + } + + /** + * Show font selection dialog. + * + * @param parent Dialog's Parent component. + * @return OK_OPTION, CANCEL_OPTION or ERROR_OPTION + * @see #OK_OPTION + * @see #CANCEL_OPTION + * @see #ERROR_OPTION + **/ + public int showDialog(Component parent) { + dialogResultValue = ERROR_OPTION; + JDialog dialog = createDialog(parent); + dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialogResultValue = CANCEL_OPTION; + } + }); + + dialog.setVisible(true); + dialog.dispose(); + + return dialogResultValue; + } + + protected class ListSelectionHandler implements ListSelectionListener { + private final JTextComponent textComponent; + + ListSelectionHandler(JTextComponent textComponent) { + this.textComponent = textComponent; + } + + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) { + JList list = (JList) e.getSource(); + String selectedValue = (String) list.getSelectedValue(); + + String oldValue = textComponent.getText(); + textComponent.setText(selectedValue); + if (!oldValue.equalsIgnoreCase(selectedValue)) { + textComponent.selectAll(); + textComponent.requestFocus(); + } + + updateSampleFont(); + } + } + } + + protected class TextFieldFocusHandlerForTextSelection extends FocusAdapter { + private final JTextComponent textComponent; + + public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) { + this.textComponent = textComponent; + } + + public void focusGained(FocusEvent e) { + textComponent.selectAll(); + } + + public void focusLost(FocusEvent e) { + textComponent.select(0, 0); + updateSampleFont(); + } + } + + protected static class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { + private final JList targetList; + + public TextFieldKeyHandlerForListSelectionUpDown(JList list) { + this.targetList = list; + } + + public void keyPressed(KeyEvent e) { + int i; + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: + i = targetList.getSelectedIndex() - 1; + if (i < 0) { + i = 0; + } + targetList.setSelectedIndex(i); + break; + case KeyEvent.VK_DOWN: + int listSize = targetList.getModel().getSize(); + i = targetList.getSelectedIndex() + 1; + if (i >= listSize) { + i = listSize - 1; + } + targetList.setSelectedIndex(i); + break; + default: + break; + } + } + } + + protected static class ListSearchTextFieldDocumentHandler implements DocumentListener { + JList targetList; + + public ListSearchTextFieldDocumentHandler(JList targetList) { + this.targetList = targetList; + } + + public void insertUpdate(DocumentEvent e) { + update(e); + } + + public void removeUpdate(DocumentEvent e) { + update(e); + } + + public void changedUpdate(DocumentEvent e) { + update(e); + } + + private void update(DocumentEvent event) { + String newValue = ""; + try { + Document doc = event.getDocument(); + newValue = doc.getText(0, doc.getLength()); + } catch (BadLocationException e) { + Luyten.showExceptionDialog("Exception!", e); + } + + if (newValue.length() > 0) { + int index = targetList.getNextMatch(newValue, 0, Position.Bias.Forward); + if (index < 0) { + index = 0; + } + targetList.ensureIndexIsVisible(index); + + String matchedName = targetList.getModel().getElementAt(index).toString(); + if (newValue.equalsIgnoreCase(matchedName)) { + if (index != targetList.getSelectedIndex()) { + SwingUtilities.invokeLater(new ListSelector(index)); + } + } + } + } + + public class ListSelector implements Runnable { + private final int index; + + public ListSelector(int index) { + this.index = index; + } + + public void run() { + targetList.setSelectedIndex(this.index); + } + } + } + + protected class DialogOKAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = 1618273732543947323L; + protected static final String ACTION_NAME = "OK"; + private final JDialog dialog; + + protected DialogOKAction(JDialog dialog) { + this.dialog = dialog; + putValue(Action.DEFAULT, ACTION_NAME); + putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); + putValue(Action.NAME, (ACTION_NAME)); + } + + public void actionPerformed(ActionEvent e) { + dialogResultValue = OK_OPTION; + dialog.setVisible(false); + } + } + + protected class DialogCancelAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = -4941763616565382601L; + protected static final String ACTION_NAME = "Cancel"; + private final JDialog dialog; + + protected DialogCancelAction(JDialog dialog) { + this.dialog = dialog; + putValue(Action.DEFAULT, ACTION_NAME); + putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); + putValue(Action.NAME, (ACTION_NAME)); + } + + public void actionPerformed(ActionEvent e) { + dialogResultValue = CANCEL_OPTION; + dialog.setVisible(false); + } + } + + protected JDialog createDialog(Component parent) { + Frame frame = parent instanceof Frame ? (Frame) parent + : (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); + JDialog dialog = new JDialog(frame, ("Select Font"), true); + + Action okAction = new DialogOKAction(dialog); + Action cancelAction = new DialogCancelAction(dialog); + + JButton okButton = new JButton(okAction); + okButton.setFont(DEFAULT_FONT); + JButton cancelButton = new JButton(cancelAction); + cancelButton.setFont(DEFAULT_FONT); + + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new GridLayout(2, 1)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + buttonsPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 10, 10)); + + ActionMap actionMap = buttonsPanel.getActionMap(); + actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction); + actionMap.put(okAction.getValue(Action.DEFAULT), okAction); + InputMap inputMap = buttonsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), cancelAction.getValue(Action.DEFAULT)); + inputMap.put(KeyStroke.getKeyStroke("ENTER"), okAction.getValue(Action.DEFAULT)); + + JPanel dialogEastPanel = new JPanel(); + dialogEastPanel.setLayout(new BorderLayout()); + dialogEastPanel.add(buttonsPanel, BorderLayout.NORTH); + + dialog.getContentPane().add(this, BorderLayout.CENTER); + dialog.getContentPane().add(dialogEastPanel, BorderLayout.EAST); + dialog.pack(); + dialog.setLocationRelativeTo(frame); + return dialog; + } + + protected void updateSampleFont() { + Font font = getSelectedFont(); + getSampleTextField().setFont(font); + } + + protected JPanel getFontFamilyPanel() { + if (fontNamePanel == null) { + fontNamePanel = new JPanel(); + fontNamePanel.setLayout(new BorderLayout()); + fontNamePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + fontNamePanel.setPreferredSize(new Dimension(180, 130)); + + JScrollPane scrollPane = new JScrollPane(getFontFamilyList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontFamilyTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Name")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontFamilyTextField()); + label.setDisplayedMnemonic('F'); + + fontNamePanel.add(label, BorderLayout.NORTH); + fontNamePanel.add(p, BorderLayout.CENTER); + + } + return fontNamePanel; + } + + protected JPanel getFontStylePanel() { + if (fontStylePanel == null) { + fontStylePanel = new JPanel(); + fontStylePanel.setLayout(new BorderLayout()); + fontStylePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + fontStylePanel.setPreferredSize(new Dimension(140, 130)); + + JScrollPane scrollPane = new JScrollPane(getFontStyleList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontStyleTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Style")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontStyleTextField()); + label.setDisplayedMnemonic('Y'); + + fontStylePanel.add(label, BorderLayout.NORTH); + fontStylePanel.add(p, BorderLayout.CENTER); + } + return fontStylePanel; + } + + protected JPanel getFontSizePanel() { + if (fontSizePanel == null) { + fontSizePanel = new JPanel(); + fontSizePanel.setLayout(new BorderLayout()); + fontSizePanel.setPreferredSize(new Dimension(70, 130)); + fontSizePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + JScrollPane scrollPane = new JScrollPane(getFontSizeList()); + scrollPane.getVerticalScrollBar().setFocusable(false); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add(getFontSizeTextField(), BorderLayout.NORTH); + p.add(scrollPane, BorderLayout.CENTER); + + JLabel label = new JLabel(("Font Size")); + label.setHorizontalAlignment(JLabel.LEFT); + label.setHorizontalTextPosition(JLabel.LEFT); + label.setLabelFor(getFontSizeTextField()); + label.setDisplayedMnemonic('S'); + + fontSizePanel.add(label, BorderLayout.NORTH); + fontSizePanel.add(p, BorderLayout.CENTER); + } + return fontSizePanel; + } + + protected JPanel getSamplePanel() { + if (samplePanel == null) { + Border titledBorder = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), ("Sample")); + Border empty = BorderFactory.createEmptyBorder(5, 10, 10, 10); + Border border = BorderFactory.createCompoundBorder(titledBorder, empty); + + samplePanel = new JPanel(); + samplePanel.setLayout(new BorderLayout()); + samplePanel.setBorder(border); + + samplePanel.add(getSampleTextField(), BorderLayout.CENTER); + } + return samplePanel; + } + + protected JTextField getSampleTextField() { + if (sampleText == null) { + Border lowered = BorderFactory.createLoweredBevelBorder(); + + sampleText = new JTextField(("AaBbYyZz")); + sampleText.setHorizontalAlignment(JTextField.CENTER); + sampleText.setBorder(lowered); + sampleText.setPreferredSize(new Dimension(300, 100)); + } + return sampleText; + } + + protected String[] getFontFamilies() { + if (fontFamilyNames == null) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + fontFamilyNames = env.getAvailableFontFamilyNames(); + } + return fontFamilyNames; + } + + protected String[] getFontStyleNames() { + if (fontStyleNames == null) { + int i = 0; + fontStyleNames = new String[4]; + fontStyleNames[i++] = ("Plain"); + fontStyleNames[i++] = ("Bold"); + fontStyleNames[i++] = ("Italic"); + fontStyleNames[i++] = ("BoldItalic"); + } + return fontStyleNames; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/JarEntryFilter.java b/src/main/java/us/deathmarine/luyten/JarEntryFilter.java new file mode 100644 index 00000000..39c4a964 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/JarEntryFilter.java @@ -0,0 +1,83 @@ +package us.deathmarine.luyten; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class JarEntryFilter { + + private JarFile jfile; + + public JarEntryFilter() { + } + + public JarEntryFilter(JarFile jfile) { + this.jfile = jfile; + } + + public List getAllEntriesFromJar() { + List mass = new ArrayList<>(); + Enumeration entries = jfile.entries(); + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + if (!e.isDirectory()) { + mass.add(e.getName()); + } + } + return mass; + } + + public List getEntriesWithoutInnerClasses() { + List mass = new ArrayList<>(); + Enumeration entries = jfile.entries(); + Set possibleInnerClasses = new HashSet<>(); + Set baseClasses = new HashSet<>(); + + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + if (!e.isDirectory()) { + String entryName = e.getName(); + + if (entryName.trim().length() > 0) { + entryName = entryName.trim(); + + if (!entryName.endsWith(".class")) { + mass.add(entryName); + + // com/acme/Model$16.class + } else if (entryName.matches(".*[^(/|\\\\)]+\\$[^(/|\\\\)]+$")) { + possibleInnerClasses.add(entryName); + + } else { + baseClasses.add(entryName); + mass.add(entryName); + } + } + } + } + + // keep Badly$Named but not inner classes + for (String inner : possibleInnerClasses) { + + // com/acme/Connection$Conn$1.class -> com/acme/Connection + String innerWithoutTail = inner.replaceAll("\\$[^(/|\\\\)]+\\.class$", ""); + if (!baseClasses.contains(innerWithoutTail + ".class")) { + mass.add(inner); + } + } + return mass; + } + + public JarFile getJfile() { + return jfile; + } + + public void setJfile(JarFile jfile) { + this.jfile = jfile; + } + +} diff --git a/src/us/deathmarine/luyten/Keymap.java b/src/main/java/us/deathmarine/luyten/Keymap.java similarity index 99% rename from src/us/deathmarine/luyten/Keymap.java rename to src/main/java/us/deathmarine/luyten/Keymap.java index c84f26ba..68196b9c 100644 --- a/src/us/deathmarine/luyten/Keymap.java +++ b/src/main/java/us/deathmarine/luyten/Keymap.java @@ -3,6 +3,7 @@ import java.awt.event.InputEvent; public final class Keymap { + /** * Ctrl+click defaults to "context menu" in macOS, so META+click is used there. * @@ -11,4 +12,5 @@ public final class Keymap { public static int ctrlDownModifier() { return SystemInfo.IS_MAC ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK; } + } diff --git a/src/main/java/us/deathmarine/luyten/LinkProvider.java b/src/main/java/us/deathmarine/luyten/LinkProvider.java new file mode 100644 index 00000000..cf104a38 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LinkProvider.java @@ -0,0 +1,22 @@ +package us.deathmarine.luyten; + +import java.util.Map; +import java.util.Set; + +public interface LinkProvider { + + void generateContent(); + + String getTextContent(); + + void processLinks(); + + Map getDefinitionToSelectionMap(); + + Map> getReferenceToSelectionsMap(); + + boolean isLinkNavigable(String uniqueStr); + + String getLinkDescription(String uniqueStr); + +} diff --git a/src/main/java/us/deathmarine/luyten/Luyten.java b/src/main/java/us/deathmarine/luyten/Luyten.java new file mode 100644 index 00000000..1c9effe9 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Luyten.java @@ -0,0 +1,269 @@ +package us.deathmarine.luyten; + +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.text.DefaultEditorKit; + +/** + * Starter, the main class + */ +public class Luyten { + + private static final AtomicReference mainWindowRef = new AtomicReference<>(); + private static final List pendingFiles = new ArrayList<>(); + private static ServerSocket lockSocket = null; + + public static void main(final String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + if (lockSocket != null) { + lockSocket.close(); + } + } catch (IOException ignored) { + } + })); + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + // for TotalCommander External Viewer setting: + // javaw -jar "c:\Program Files\Luyten\luyten.jar" + // (TC will not complain about temporary file when opening .class from + // .zip or .jar) + final File fileFromCommandLine = getFileFromCommandLine(args); + + try { + launchMainInstance(fileFromCommandLine); + } catch (Exception e) { + // Instance already exists. Open new file in running instance + try { + Socket socket = new Socket("localhost", 3456); + DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); + dos.writeUTF(args[0]); + dos.flush(); + dos.close(); + socket.close(); + } catch (IOException ex) { + showExceptionDialog("Exception", e); + } + } + } + + private static void launchMainInstance(final File fileFromCommandLine) throws IOException { + lockSocket = new ServerSocket(3456); + launchSession(fileFromCommandLine); + new Thread(Luyten::launchServer).start(); + } + + private static void launchSession(final File fileFromCommandLine) { + SwingUtilities.invokeLater(() -> { + if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { + // Already set - so add the files to open + addToPendingFiles(fileFromCommandLine); + } + processPendingFiles(); + mainWindowRef.get().setVisible(true); + }); + } + + private static void launchServer() { + try { // Server + while (true) { + Socket socket = lockSocket.accept(); + DataInputStream dis = new DataInputStream(socket.getInputStream()); + addToPendingFiles(getFileFromCommandLine(dis.readUTF())); + processPendingFiles(); + dis.close(); + socket.close(); + } + } catch (SocketException ignored) { + // Ignore exception on shutdown + } catch (IOException e) { // Client + showExceptionDialog("Exception", e); + } + } + + // Private function which processes all pending files - synchronized on the + // list of pending files + public static void processPendingFiles() { + final MainWindow mainWindow = mainWindowRef.get(); + if (mainWindow != null) { + synchronized (pendingFiles) { + for (File f : pendingFiles) { + mainWindow.loadNewFile(f); + } + pendingFiles.clear(); + } + } + } + + // Function which opens the given file in the instance, if it's running - + // and if not, it processes the files + public static void addToPendingFiles(File fileToOpen) { + synchronized (pendingFiles) { + if (fileToOpen != null) { + pendingFiles.add(fileToOpen); + } + } + } + + // Function which exits the application if it's running + public static void quitInstance() { + final MainWindow mainWindow = mainWindowRef.get(); + if (mainWindow != null) { + mainWindow.onExitMenu(); + } + } + + public static File getFileFromCommandLine(String... args) { + File fileFromCommandLine = null; + try { + if (args.length > 0) { + String realFileName = new File(args[0]).getCanonicalPath(); + fileFromCommandLine = new File(realFileName); + } + } catch (Exception e) { + e.printStackTrace(); + } + return fileFromCommandLine; + } + + public static String getVersion() { + String result = ""; + try { + String line; + BufferedReader br = new BufferedReader(new InputStreamReader( + ClassLoader.getSystemResourceAsStream("META-INF/maven/us.deathmarine/luyten/pom.properties"))); + while ((line = br.readLine()) != null) { + if (line.contains("version")) + result = line.split("=")[1]; + } + br.close(); + } catch (Exception e) { + return result; + } + return result; + + } + + /** + * Method allows for users to copy the stacktrace for reporting any issues. + * Add Cool Hyperlink Enhanced for mouse users. + * + * @param message + * @param e + */ + public static void showExceptionDialog(String message, Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + String stacktrace = sw.toString(); + try { + sw.close(); + pw.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + System.out.println(stacktrace); + + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + if (message.contains("\n")) { + for (String s : message.split("\n")) { + pane.add(new JLabel(s)); + } + } else { + pane.add(new JLabel(message)); + } + pane.add(new JLabel(" \n")); // Whitespace + final JTextArea exception = new JTextArea(25, 100); + exception.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 10)); + exception.setText(stacktrace); + exception.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + new JPopupMenu() { + { + JMenuItem menuitem = new JMenuItem("Select All"); + menuitem.addActionListener(e12 -> { + exception.requestFocus(); + exception.selectAll(); + }); + this.add(menuitem); + menuitem = new JMenuItem("Copy"); + menuitem.addActionListener(new DefaultEditorKit.CopyAction()); + this.add(menuitem); + } + + private static final long serialVersionUID = 562054483562666832L; + }.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + JScrollPane scroll = new JScrollPane(exception); + scroll.setBorder(new CompoundBorder(BorderFactory.createTitledBorder("Stacktrace"), + new BevelBorder(BevelBorder.LOWERED))); + pane.add(scroll); + final String issue = "https://github.com/deathmarine/Luyten/issues"; + final JLabel link = new JLabel("Submit to " + issue + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(issue)); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + link.setText("Submit to " + issue + ""); + } + + @Override + public void mouseExited(MouseEvent e) { + link.setText("Submit to " + issue + ""); + } + }); + pane.add(link); + JOptionPane.showMessageDialog(null, pane, "Error!", JOptionPane.ERROR_MESSAGE); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenOsx.java b/src/main/java/us/deathmarine/luyten/LuytenOsx.java new file mode 100644 index 00000000..3a574fae --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenOsx.java @@ -0,0 +1,33 @@ +package us.deathmarine.luyten; + +import com.apple.eawt.Application; +import com.apple.eawt.ApplicationAdapter; +import com.apple.eawt.ApplicationEvent; +import java.io.File; + +/** + * An OS X-specific initialization method for dragging/dropping + */ +public class LuytenOsx extends Luyten { + + public static void main(String[] args) { + // Add an adapter as the handler to a new instance of the application + // class + @SuppressWarnings("deprecation") + Application app = new Application(); + app.addApplicationListener(new ApplicationAdapter() { + public void handleOpenFile(ApplicationEvent e) { + Luyten.addToPendingFiles(new File(e.getFilename())); + Luyten.processPendingFiles(); + } + + public void handleQuit(ApplicationEvent e) { + Luyten.quitInstance(); + } + }); + + // Call the superclass's main function + Luyten.main(args); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenPreferences.java b/src/main/java/us/deathmarine/luyten/LuytenPreferences.java new file mode 100644 index 00000000..09b6a9e7 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenPreferences.java @@ -0,0 +1,89 @@ +package us.deathmarine.luyten; + +/** + * Do not instantiate this class, get the instance from ConfigSaver. All + * not-static fields will be saved automatically named by the field's java + * variable name. (Watch for collisions with existing IDs defined in + * ConfigSaver.) Only String, boolean and int fields are supported. Write + * default values into the field declarations. + */ +public class LuytenPreferences { + + public static final String THEME_XML_PATH = "/org/fife/ui/rsyntaxtextarea/themes/"; + public static final String DEFAULT_THEME_XML = "eclipse.xml"; + + private String themeXml = DEFAULT_THEME_XML; + private String fileOpenCurrentDirectory = ""; + private String fileSaveCurrentDirectory = ""; + private int font_size = 10; + + private boolean isPackageExplorerStyle = true; + private boolean isFilterOutInnerClassEntries = true; + private boolean isSingleClickOpenEnabled = true; + private boolean isExitByEscEnabled = false; + + public String getThemeXml() { + return themeXml; + } + + public void setThemeXml(String themeXml) { + this.themeXml = themeXml; + } + + public String getFileOpenCurrentDirectory() { + return fileOpenCurrentDirectory; + } + + public void setFileOpenCurrentDirectory(String fileOpenCurrentDirectory) { + this.fileOpenCurrentDirectory = fileOpenCurrentDirectory; + } + + public String getFileSaveCurrentDirectory() { + return fileSaveCurrentDirectory; + } + + public void setFileSaveCurrentDirectory(String fileSaveCurrentDirectory) { + this.fileSaveCurrentDirectory = fileSaveCurrentDirectory; + } + + public boolean isPackageExplorerStyle() { + return isPackageExplorerStyle; + } + + public void setPackageExplorerStyle(boolean isPackageExplorerStyle) { + this.isPackageExplorerStyle = isPackageExplorerStyle; + } + + public boolean isFilterOutInnerClassEntries() { + return isFilterOutInnerClassEntries; + } + + public void setFilterOutInnerClassEntries(boolean isFilterOutInnerClassEntries) { + this.isFilterOutInnerClassEntries = isFilterOutInnerClassEntries; + } + + public boolean isSingleClickOpenEnabled() { + return isSingleClickOpenEnabled; + } + + public void setSingleClickOpenEnabled(boolean isSingleClickOpenEnabled) { + this.isSingleClickOpenEnabled = isSingleClickOpenEnabled; + } + + public boolean isExitByEscEnabled() { + return isExitByEscEnabled; + } + + public void setExitByEscEnabled(boolean isExitByEscEnabled) { + this.isExitByEscEnabled = isExitByEscEnabled; + } + + public int getFont_size() { + return font_size; + } + + public void setFont_size(int font_size) { + this.font_size = font_size; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java b/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java new file mode 100644 index 00000000..9005b46e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/LuytenTypeLoader.java @@ -0,0 +1,35 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.InputTypeLoader; +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ITypeLoader; +import java.util.ArrayList; +import java.util.List; + +public final class LuytenTypeLoader implements ITypeLoader { + + private final List _typeLoaders; + + public LuytenTypeLoader() { + _typeLoaders = new ArrayList<>(); + _typeLoaders.add(new InputTypeLoader()); + } + + public List getTypeLoaders() { + return _typeLoaders; + } + + @Override + public boolean tryLoadType(final String internalName, final Buffer buffer) { + for (final ITypeLoader typeLoader : _typeLoaders) { + if (typeLoader.tryLoadType(internalName, buffer)) { + return true; + } + + buffer.reset(); + } + + return false; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/MainMenuBar.java b/src/main/java/us/deathmarine/luyten/MainMenuBar.java new file mode 100644 index 00000000..7ffd928c --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/MainMenuBar.java @@ -0,0 +1,587 @@ +package us.deathmarine.luyten; + +import com.strobel.Procyon; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.languages.Language; +import com.strobel.decompiler.languages.Languages; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.ListIterator; +import java.util.Map; +import javax.swing.AbstractAction; +import javax.swing.AbstractButton; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ButtonModel; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; +import javax.swing.text.DefaultEditorKit; + +/** + * Main menu (only MainWindow should be called from here) + */ +public class MainMenuBar extends JMenuBar { + + private static final long serialVersionUID = -7949855817172562075L; + private final MainWindow mainWindow; + private final Map languageLookup = new HashMap<>(); + + private JMenu recentFiles; + private JMenuItem clearRecentFiles; + private JCheckBoxMenuItem flattenSwitchBlocks; + private JCheckBoxMenuItem forceExplicitImports; + private JCheckBoxMenuItem forceExplicitTypes; + private JCheckBoxMenuItem showSyntheticMembers; + private JCheckBoxMenuItem excludeNestedTypes; + private JCheckBoxMenuItem retainRedundantCasts; + private JCheckBoxMenuItem unicodeReplacement; + private JCheckBoxMenuItem debugLineNumbers; + private JCheckBoxMenuItem showDebugInfo; + private JCheckBoxMenuItem bytecodeLineNumbers; + private JRadioButtonMenuItem java; + private JRadioButtonMenuItem bytecode; + private JRadioButtonMenuItem bytecodeAST; + private ButtonGroup languagesGroup; + private ButtonGroup themesGroup; + private JCheckBoxMenuItem packageExplorerStyle; + private JCheckBoxMenuItem filterOutInnerClassEntries; + private JCheckBoxMenuItem singleClickOpenEnabled; + private JCheckBoxMenuItem exitByEscEnabled; + private final DecompilerSettings settings; + private final LuytenPreferences luytenPrefs; + + public MainMenuBar(MainWindow mainWnd) { + this.mainWindow = mainWnd; + final ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); + settings = configSaver.getDecompilerSettings(); + luytenPrefs = configSaver.getLuytenPreferences(); + + final JMenu fileMenu = new JMenu("File"); + fileMenu.add(new JMenuItem("...")); + this.add(fileMenu); + final JMenu editMenu = new JMenu("Edit"); + editMenu.add(new JMenuItem("...")); + this.add(editMenu); + final JMenu themesMenu = new JMenu("Themes"); + themesMenu.add(new JMenuItem("...")); + this.add(themesMenu); + final JMenu operationMenu = new JMenu("Operation"); + operationMenu.add(new JMenuItem("...")); + this.add(operationMenu); + final JMenu settingsMenu = new JMenu("Settings"); + settingsMenu.add(new JMenuItem("...")); + this.add(settingsMenu); + final JMenu helpMenu = new JMenu("Help"); + helpMenu.add(new JMenuItem("...")); + this.add(helpMenu); + + // start quicker + new Thread() { + public void run() { + try { + // build menu later + buildFileMenu(fileMenu); + refreshMenuPopup(fileMenu); + + buildEditMenu(editMenu); + refreshMenuPopup(editMenu); + + buildThemesMenu(themesMenu); + refreshMenuPopup(themesMenu); + + buildOperationMenu(operationMenu); + refreshMenuPopup(operationMenu); + + buildSettingsMenu(settingsMenu); + refreshMenuPopup(settingsMenu); + + buildHelpMenu(helpMenu); + refreshMenuPopup(helpMenu); + + updateRecentFiles(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + // refresh currently opened menu + // (if user selected a menu before it was ready) + private void refreshMenuPopup(JMenu menu) { + try { + if (menu.isPopupMenuVisible()) { + menu.getPopupMenu().setVisible(false); + menu.getPopupMenu().setVisible(true); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + }.start(); + } + + public void updateRecentFiles() { + if (RecentFiles.paths.isEmpty()) { + recentFiles.setEnabled(false); + clearRecentFiles.setEnabled(false); + return; + } else { + recentFiles.setEnabled(true); + clearRecentFiles.setEnabled(true); + } + + recentFiles.removeAll(); + ListIterator li = RecentFiles.paths.listIterator(RecentFiles.paths.size()); + boolean rfSaveNeeded = false; + + while (li.hasPrevious()) { + String path = li.previous(); + final File file = new File(path); + + if (!file.exists()) { + rfSaveNeeded = true; + continue; + } + + JMenuItem menuItem = new JMenuItem(path); + menuItem.addActionListener(e -> mainWindow.loadNewFile(file)); + recentFiles.add(menuItem); + } + + if (rfSaveNeeded) RecentFiles.save(); + } + + private void buildFileMenu(final JMenu fileMenu) { + fileMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Open File..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onOpenFileMenu()); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + menuItem = new JMenuItem("Close File"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> { + JTabbedPane house = mainWindow.getSelectedModel().house; + + if (e.getModifiers() != InputEvent.CTRL_MASK || house.getTabCount() == 0) + mainWindow.onCloseFileMenu(); + else { + mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); + } + }); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + menuItem = new JMenuItem("Save As..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSaveAsMenu()); + fileMenu.add(menuItem); + + menuItem = new JMenuItem("Save All..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSaveAllMenu()); + fileMenu.add(menuItem); + fileMenu.addSeparator(); + + recentFiles = new JMenu("Recent Files"); + fileMenu.add(recentFiles); + + clearRecentFiles = new JMenuItem("Clear Recent Files"); + clearRecentFiles.addActionListener(e -> { + RecentFiles.paths.clear(); + RecentFiles.save(); + updateRecentFiles(); + }); + fileMenu.add(clearRecentFiles); + + fileMenu.addSeparator(); + + // Only add the exit command for non-OS X. OS X handles its close + // automatically + if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { + menuItem = new JMenuItem("Exit"); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); + menuItem.addActionListener(e -> mainWindow.onExitMenu()); + fileMenu.add(menuItem); + } + } + + private void buildEditMenu(JMenu editMenu) { + editMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Cut"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.setEnabled(false); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Copy"); + menuItem.addActionListener(new DefaultEditorKit.CopyAction()); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Paste"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.setEnabled(false); + editMenu.add(menuItem); + + editMenu.addSeparator(); + + menuItem = new JMenuItem("Select All"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onSelectAllMenu()); + editMenu.add(menuItem); + editMenu.addSeparator(); + + menuItem = new JMenuItem("Find..."); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onFindMenu()); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find Next"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); + menuItem.addActionListener(e -> { + if (mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); + }); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find Previous"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)); + menuItem.addActionListener(e -> { + if (mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); + }); + editMenu.add(menuItem); + + menuItem = new JMenuItem("Find All"); + menuItem.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menuItem.addActionListener(e -> mainWindow.onFindAllMenu()); + editMenu.add(menuItem); + } + + private void buildThemesMenu(JMenu themesMenu) { + themesMenu.removeAll(); + themesGroup = new ButtonGroup(); + JRadioButtonMenuItem a = new JRadioButtonMenuItem(new ThemeAction("Default", "default.xml")); + a.setSelected("default.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Default-Alt", "default-alt.xml")); + a.setSelected("default-alt.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Dark", "dark.xml")); + a.setSelected("dark.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Eclipse", "eclipse.xml")); + a.setSelected("eclipse.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("Visual Studio", "vs.xml")); + a.setSelected("vs.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + + a = new JRadioButtonMenuItem(new ThemeAction("IntelliJ", "idea.xml")); + a.setSelected("idea.xml".equals(luytenPrefs.getThemeXml())); + themesGroup.add(a); + themesMenu.add(a); + } + + private void buildOperationMenu(JMenu operationMenu) { + operationMenu.removeAll(); + packageExplorerStyle = new JCheckBoxMenuItem("Package Explorer Style"); + packageExplorerStyle.setSelected(luytenPrefs.isPackageExplorerStyle()); + packageExplorerStyle.addActionListener(e -> { + luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); + operationMenu.add(packageExplorerStyle); + + filterOutInnerClassEntries = new JCheckBoxMenuItem("Filter Out Inner Class Entries"); + filterOutInnerClassEntries.setSelected(luytenPrefs.isFilterOutInnerClassEntries()); + filterOutInnerClassEntries.addActionListener(e -> { + luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); + mainWindow.onTreeSettingsChanged(); + }); + operationMenu.add(filterOutInnerClassEntries); + + singleClickOpenEnabled = new JCheckBoxMenuItem("Single Click Open"); + singleClickOpenEnabled.setSelected(luytenPrefs.isSingleClickOpenEnabled()); + singleClickOpenEnabled.addActionListener(e -> luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected())); + operationMenu.add(singleClickOpenEnabled); + + exitByEscEnabled = new JCheckBoxMenuItem("Exit By Esc"); + exitByEscEnabled.setSelected(luytenPrefs.isExitByEscEnabled()); + exitByEscEnabled.addActionListener(e -> luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected())); + operationMenu.add(exitByEscEnabled); + } + + private void buildSettingsMenu(JMenu settingsMenu) { + settingsMenu.removeAll(); + ActionListener settingsChanged = e -> new Thread(() -> { + populateSettingsFromSettingsMenu(); + mainWindow.onSettingsChanged(); + }).start(); + flattenSwitchBlocks = new JCheckBoxMenuItem("Flatten Switch Blocks"); + flattenSwitchBlocks.setSelected(settings.getFlattenSwitchBlocks()); + flattenSwitchBlocks.addActionListener(settingsChanged); + settingsMenu.add(flattenSwitchBlocks); + + forceExplicitImports = new JCheckBoxMenuItem("Force Explicit Imports"); + forceExplicitImports.setSelected(settings.getForceExplicitImports()); + forceExplicitImports.addActionListener(settingsChanged); + settingsMenu.add(forceExplicitImports); + + forceExplicitTypes = new JCheckBoxMenuItem("Force Explicit Types"); + forceExplicitTypes.setSelected(settings.getForceExplicitTypeArguments()); + forceExplicitTypes.addActionListener(settingsChanged); + settingsMenu.add(forceExplicitTypes); + + showSyntheticMembers = new JCheckBoxMenuItem("Show Synthetic Members"); + showSyntheticMembers.setSelected(settings.getShowSyntheticMembers()); + showSyntheticMembers.addActionListener(settingsChanged); + settingsMenu.add(showSyntheticMembers); + + excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types"); + excludeNestedTypes.setSelected(settings.getExcludeNestedTypes()); + excludeNestedTypes.addActionListener(settingsChanged); + settingsMenu.add(excludeNestedTypes); + + retainRedundantCasts = new JCheckBoxMenuItem("Retain Redundant Casts"); + retainRedundantCasts.setSelected(settings.getRetainRedundantCasts()); + retainRedundantCasts.addActionListener(settingsChanged); + settingsMenu.add(retainRedundantCasts); + + unicodeReplacement = new JCheckBoxMenuItem("Enable Unicode Replacement"); + unicodeReplacement.setSelected(settings.isUnicodeOutputEnabled()); + unicodeReplacement.addActionListener(settingsChanged); + settingsMenu.add(unicodeReplacement); + + debugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers"); + debugLineNumbers.setSelected(settings.getShowDebugLineNumbers()); + debugLineNumbers.addActionListener(settingsChanged); + settingsMenu.add(debugLineNumbers); + + JMenu debugSettingsMenu = new JMenu("Debug Settings"); + showDebugInfo = new JCheckBoxMenuItem("Include Error Diagnostics"); + showDebugInfo.setSelected(settings.getIncludeErrorDiagnostics()); + showDebugInfo.addActionListener(settingsChanged); + + debugSettingsMenu.add(showDebugInfo); + settingsMenu.add(debugSettingsMenu); + settingsMenu.addSeparator(); + + languageLookup.put(Languages.java().getName(), Languages.java()); + languageLookup.put(Languages.bytecode().getName(), Languages.bytecode()); + languageLookup.put(Languages.bytecodeAst().getName(), Languages.bytecodeAst()); + + languagesGroup = new ButtonGroup(); + java = new JRadioButtonMenuItem(Languages.java().getName()); + java.getModel().setActionCommand(Languages.java().getName()); + java.setSelected(Languages.java().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(java); + settingsMenu.add(java); + bytecode = new JRadioButtonMenuItem(Languages.bytecode().getName()); + bytecode.getModel().setActionCommand(Languages.bytecode().getName()); + bytecode.setSelected(Languages.bytecode().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(bytecode); + settingsMenu.add(bytecode); + bytecodeAST = new JRadioButtonMenuItem(Languages.bytecodeAst().getName()); + bytecodeAST.getModel().setActionCommand(Languages.bytecodeAst().getName()); + bytecodeAST.setSelected(Languages.bytecodeAst().getName().equals(settings.getLanguage().getName())); + languagesGroup.add(bytecodeAST); + settingsMenu.add(bytecodeAST); + + JMenu debugLanguagesMenu = new JMenu("Debug Languages"); + for (final Language language : Languages.debug()) { + final JRadioButtonMenuItem m = new JRadioButtonMenuItem(language.getName()); + m.getModel().setActionCommand(language.getName()); + m.setSelected(language.getName().equals(settings.getLanguage().getName())); + languagesGroup.add(m); + debugLanguagesMenu.add(m); + languageLookup.put(language.getName(), language); + } + for (AbstractButton button : Collections.list(languagesGroup.getElements())) { + button.addActionListener(settingsChanged); + } + settingsMenu.add(debugLanguagesMenu); + + bytecodeLineNumbers = new JCheckBoxMenuItem("Show Line Numbers In Bytecode"); + bytecodeLineNumbers.setSelected(settings.getIncludeLineNumbersInBytecode()); + bytecodeLineNumbers.addActionListener(settingsChanged); + settingsMenu.add(bytecodeLineNumbers); + } + + private void buildHelpMenu(JMenu helpMenu) { + helpMenu.removeAll(); + JMenuItem menuItem = new JMenuItem("Legal"); + menuItem.addActionListener(e -> mainWindow.onLegalMenu()); + helpMenu.add(menuItem); + JMenu menuDebug = new JMenu("Debug"); + menuItem = new JMenuItem("List JVM Classes"); + menuItem.addActionListener(e -> mainWindow.onListLoadedClasses()); + menuDebug.add(menuItem); + helpMenu.add(menuDebug); + menuItem = new JMenuItem("About"); + menuItem.addActionListener(event -> { + JPanel pane = new JPanel(); + pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); + JLabel title = new JLabel("Luyten " + Luyten.getVersion()); + title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); + pane.add(title); + pane.add(new JLabel("by Deathmarine")); + String project = "https://github.com/deathmarine/Luyten/"; + JLabel link = new JLabel("" + project + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(project, link)); + pane.add(link); + pane.add(new JLabel("Contributions By:")); + pane.add(new JLabel("zerdei, toonetown, dstmath")); + pane.add(new JLabel("virustotalop, xtrafrancyz,")); + pane.add(new JLabel("mbax, quitten, mstrobel,")); + pane.add(new JLabel("FisheyLP, Syquel, and ThexXTURBOXx")); + pane.add(new JLabel(" ")); + pane.add(new JLabel("Powered By:")); + String procyon = "https://github.com/mstrobel/procyon"; + link = new JLabel("" + procyon + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(procyon, link)); + pane.add(link); + pane.add(new JLabel("Version: " + Procyon.version())); + pane.add(new JLabel("(c) 2022 Mike Strobel")); + String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; + link = new JLabel("" + rsyntax + ""); + link.setCursor(new Cursor(Cursor.HAND_CURSOR)); + link.addMouseListener(new LinkListener(rsyntax, link)); + pane.add(link); + pane.add(new JLabel("Version: 3.1.5")); + pane.add(new JLabel("(c) 2022 Robert Futrell")); + pane.add(new JLabel(" ")); + JOptionPane.showMessageDialog(null, pane); + }); + helpMenu.add(menuItem); + } + + private void populateSettingsFromSettingsMenu() { + // synchronized: do not disturb decompiler at work (synchronize every + // time before run decompiler) + synchronized (settings) { + settings.setFlattenSwitchBlocks(flattenSwitchBlocks.isSelected()); + settings.setForceExplicitImports(forceExplicitImports.isSelected()); + settings.setShowSyntheticMembers(showSyntheticMembers.isSelected()); + settings.setExcludeNestedTypes(excludeNestedTypes.isSelected()); + settings.setForceExplicitTypeArguments(forceExplicitTypes.isSelected()); + settings.setRetainRedundantCasts(retainRedundantCasts.isSelected()); + settings.setIncludeErrorDiagnostics(showDebugInfo.isSelected()); + settings.setUnicodeOutputEnabled(unicodeReplacement.isSelected()); + settings.setShowDebugLineNumbers(debugLineNumbers.isSelected()); + // + // Note: You shouldn't ever need to set this. It's only for + // languages that support catch + // blocks without an exception variable. Java doesn't allow this. I + // think Scala does. + // + // settings.setAlwaysGenerateExceptionVariableForCatchBlocks(true); + // + + final ButtonModel selectedLanguage = languagesGroup.getSelection(); + if (selectedLanguage != null) { + final Language language = languageLookup.get(selectedLanguage.getActionCommand()); + + if (language != null) + settings.setLanguage(language); + } + + if (java.isSelected()) { + settings.setLanguage(Languages.java()); + } else if (bytecode.isSelected()) { + settings.setLanguage(Languages.bytecode()); + } else if (bytecodeAST.isSelected()) { + settings.setLanguage(Languages.bytecodeAst()); + } + settings.setIncludeLineNumbersInBytecode(bytecodeLineNumbers.isSelected()); + } + } + + private class ThemeAction extends AbstractAction { + private static final long serialVersionUID = -6618680171943723199L; + private final String xml; + + public ThemeAction(String name, String xml) { + putValue(NAME, name); + this.xml = xml; + } + + @Override + public void actionPerformed(ActionEvent e) { + luytenPrefs.setThemeXml(xml); + mainWindow.onThemesChanged(); + } + } + + private static class LinkListener extends MouseAdapter { + String link; + JLabel label; + + public LinkListener(String link, JLabel label) { + this.link = link; + this.label = label; + } + + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(link)); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + label.setText("" + link + ""); + } + + @Override + public void mouseExited(MouseEvent e) { + label.setText("" + link + ""); + } + + } + +} diff --git a/src/main/java/us/deathmarine/luyten/MainWindow.java b/src/main/java/us/deathmarine/luyten/MainWindow.java new file mode 100644 index 00000000..db4a9961 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/MainWindow.java @@ -0,0 +1,520 @@ +package us.deathmarine.luyten; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Toolkit; +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.border.BevelBorder; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; + +/** + * Dispatcher + */ +public class MainWindow extends JFrame { + + private static final long serialVersionUID = 5265556630724988013L; + + private static final String TITLE = "Luyten"; + private static final String DEFAULT_TAB = "#DEFAULT"; + + private final JProgressBar bar; + private final JLabel label; + FindBox findBox; + private FindAllBox findAllBox; + private final ConfigSaver configSaver; + private final WindowPosition windowPosition; + private final LuytenPreferences luytenPrefs; + private final FileDialog fileDialog; + private final FileSaver fileSaver; + private final JTabbedPane jarsTabbedPane; + private final Map jarModels; + public MainMenuBar mainMenuBar; + + public MainWindow(File fileFromCommandLine) { + configSaver = ConfigSaver.getLoadedInstance(); + windowPosition = configSaver.getMainWindowPosition(); + luytenPrefs = configSaver.getLuytenPreferences(); + + jarModels = new HashMap<>(); + mainMenuBar = new MainMenuBar(this); + this.setJMenuBar(mainMenuBar); + + this.adjustWindowPositionBySavedState(); + this.setHideFindBoxOnMainWindowFocus(); + this.setShowFindAllBoxOnMainWindowFocus(); + this.setQuitOnWindowClosing(); + this.setTitle(TITLE); + this.setIconImage(new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/Luyten.png"))).getImage()); + + JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); + label = new JLabel(); + label.setHorizontalAlignment(JLabel.LEFT); + panel1.setBorder(new BevelBorder(BevelBorder.LOWERED)); + panel1.setPreferredSize(new Dimension(this.getWidth() / 2, 20)); + panel1.add(label); + + JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + bar = new JProgressBar(); + + bar.setStringPainted(true); + bar.setOpaque(false); + bar.setVisible(false); + panel2.setPreferredSize(new Dimension(this.getWidth() / 3, 20)); + panel2.add(bar); + + jarsTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + jarsTabbedPane.setUI(new BasicTabbedPaneUI() { + @Override + protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) { + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) == -1) + return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height); + else + return 0; + } + }); + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + this.getContentPane().add(jarsTabbedPane); + + JSplitPane spt = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, panel2) { + private static final long serialVersionUID = 2189946972124687305L; + private final int location = 400; + + { + setDividerLocation(location); + } + + @Override + public int getDividerLocation() { + return location; + } + + @Override + public int getLastDividerLocation() { + return location; + } + }; + spt.setBorder(new BevelBorder(BevelBorder.LOWERED)); + spt.setPreferredSize(new Dimension(this.getWidth(), 24)); + this.add(spt, BorderLayout.SOUTH); + Model jarModel = null; + if (fileFromCommandLine != null) { + jarModel = loadNewFile(fileFromCommandLine); + } + + try { + DropTarget dt = new DropTarget(); + dt.addDropTargetListener(new DropListener(this)); + this.setDropTarget(dt); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + + fileDialog = new FileDialog(this); + fileSaver = new FileSaver(bar, label); + + if (jarModel != null) { + this.setExitOnEscWhenEnabled(jarModel); + } + + if (jarModel != null && (fileFromCommandLine.getName().toLowerCase().endsWith(".jar") + || fileFromCommandLine.getName().toLowerCase().endsWith(".zip"))) { + jarModel.startWarmUpThread(); + } + + if (RecentFiles.load() > 0) mainMenuBar.updateRecentFiles(); + } + + private void createDefaultTab() { + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + } + + private void removeDefaultTab() { + jarsTabbedPane.remove(jarsTabbedPane.indexOfTab(DEFAULT_TAB)); + } + + public void onOpenFileMenu() { + File selectedFile = fileDialog.doOpenDialog(); + if (selectedFile != null) { + System.out.println("[Open]: Opening " + selectedFile.getAbsolutePath()); + this.loadNewFile(selectedFile); + } + } + + public Model loadNewFile(final File file) { + // In case we open the same file again + // we remove the old entry to force a refresh + if (jarModels.containsKey(file.getAbsolutePath())) { + jarModels.remove(file.getAbsolutePath()); + int index = jarsTabbedPane.indexOfTab(file.getName()); + jarsTabbedPane.remove(index); + } + + final Model jarModel = new Model(this); + jarModel.loadFile(file); + jarModels.put(file.getAbsolutePath(), jarModel); + jarsTabbedPane.addTab(file.getName(), jarModel); + jarsTabbedPane.setSelectedComponent(jarModel); + + final String tabName = file.getName(); + int index = jarsTabbedPane.indexOfTab(tabName); + Model.Tab tabUI = new Model.Tab(tabName, () -> { + int index1 = jarsTabbedPane.indexOfTab(tabName); + jarModels.remove(file.getAbsolutePath()); + jarsTabbedPane.remove(index1); + jarModel.closeFile(); + if (jarsTabbedPane.getTabCount() == 0) { + createDefaultTab(); + } + }); + jarsTabbedPane.setTabComponentAt(index, tabUI); + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { + removeDefaultTab(); + } + return jarModel; + } + + public void onCloseFileMenu() { + this.getSelectedModel().closeFile(); + jarModels.remove(getSelectedModel()); + } + + public void onSaveAsMenu() { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane == null) + return; + String tabTitle = this.getSelectedModel().getCurrentTabTitle(); + if (tabTitle == null) + return; + + String recommendedFileName = tabTitle.replace(".class", ".java"); + File selectedFile = fileDialog.doSaveDialog(recommendedFileName); + if (selectedFile != null) { + fileSaver.saveText(pane.getText(), selectedFile); + } + } + + public void onSaveAllMenu() { + File openedFile = this.getSelectedModel().getOpenedFile(); + if (openedFile == null) + return; + + String fileName = openedFile.getName(); + if (fileName.endsWith(".class")) { + fileName = fileName.replace(".class", ".java"); + } else if (fileName.toLowerCase().endsWith(".jar")) { + fileName = "decompiled-" + fileName.replaceAll("\\.[jJ][aA][rR]", ".zip"); + } else { + fileName = "saved-" + fileName; + } + + File selectedFileToSave = fileDialog.doSaveAllDialog(fileName); + if (selectedFileToSave != null) { + fileSaver.saveAllDecompiled(openedFile, selectedFileToSave); + } + } + + public void onExitMenu() { + quit(); + } + + public void onSelectAllMenu() { + try { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + pane.requestFocusInWindow(); + pane.setSelectionStart(0); + pane.setSelectionEnd(pane.getText().length()); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onFindMenu() { + try { + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); + if (pane != null) { + if (findBox == null) + findBox = new FindBox(this); + findBox.showFindBox(); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onFindAllMenu() { + try { + if (findAllBox == null) + findAllBox = new FindAllBox(this); + findAllBox.showFindBox(); + + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onLegalMenu() { + new Thread(() -> { + try { + bar.setVisible(true); + bar.setIndeterminate(true); + String legalStr = getLegalStr(); + getSelectedModel().showLegal(legalStr); + } finally { + bar.setIndeterminate(false); + bar.setVisible(false); + } + }).start(); + } + + public void onListLoadedClasses() { + try { + StringBuilder sb = new StringBuilder(); + ClassLoader myCL = Thread.currentThread().getContextClassLoader(); + bar.setVisible(true); + bar.setIndeterminate(true); + while (myCL != null) { + sb.append("ClassLoader: ").append(myCL).append("\n"); + for (Iterator iter = list(myCL); iter.hasNext(); ) { + sb.append("\t").append(iter.next()).append("\n"); + } + myCL = myCL.getParent(); + } + this.getSelectedModel().show("Debug", sb.toString()); + } finally { + bar.setIndeterminate(false); + bar.setVisible(false); + } + } + + private static Iterator list(ClassLoader CL) { + Class CL_class = CL.getClass(); + while (CL_class != java.lang.ClassLoader.class) { + CL_class = CL_class.getSuperclass(); + } + java.lang.reflect.Field ClassLoader_classes_field; + try { + ClassLoader_classes_field = CL_class.getDeclaredField("classes"); + ClassLoader_classes_field.setAccessible(true); + Iterable classes = (Iterable) ClassLoader_classes_field.get(CL); + return classes.iterator(); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + Luyten.showExceptionDialog("Exception!", e); + } + return null; + } + + private String getLegalStr() { + StringBuilder sb = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader( + new InputStreamReader(getClass().getResourceAsStream("/distfiles/Procyon.License.txt"))); + String line; + while ((line = reader.readLine()) != null) + sb.append(line).append("\n"); + sb.append("\n\n\n\n\n"); + reader = new BufferedReader( + new InputStreamReader(getClass().getResourceAsStream("/distfiles/RSyntaxTextArea.License.txt"))); + while ((line = reader.readLine()) != null) + sb.append(line).append("\n"); + } catch (IOException e) { + Luyten.showExceptionDialog("Exception!", e); + } + return sb.toString(); + } + + public void onThemesChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.changeTheme(luytenPrefs.getThemeXml()); + luytenPrefs.setFont_size(jarModel.getTheme().baseFont.getSize()); + } + } + + public void onSettingsChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.updateOpenClasses(); + } + } + + public void onTreeSettingsChanged() { + for (Model jarModel : jarModels.values()) { + jarModel.updateTree(); + } + } + + public void onFilesDropped(List files) { + if (files != null) { + for (File file : files) { + this.loadNewFile(file); + } + } + } + + public void onFileDropped(File file) { + if (file != null) { + this.loadNewFile(file); + } + } + + public void onFileLoadEnded() { + try { + Model model = getSelectedModel(); + if (model != null && model.getFileName() != null) { + this.setTitle(TITLE + " - " + model.getFileName()); + } else { + this.setTitle(TITLE); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + public void onNavigationRequest(String uniqueStr) { + this.getSelectedModel().navigateTo(uniqueStr); + } + + private void adjustWindowPositionBySavedState() { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + if (!windowPosition.isSavedWindowPositionValid()) { + final Dimension center = new Dimension((int) (screenSize.width * 0.75), (int) (screenSize.height * 0.75)); + final int x = (int) (center.width * 0.2); + final int y = (int) (center.height * 0.2); + this.setBounds(x, y, center.width, center.height); + + } else if (windowPosition.isFullScreen()) { + int heightMinusTray = screenSize.height; + if (screenSize.height > 30) + heightMinusTray -= 30; + this.setBounds(0, 0, screenSize.width, heightMinusTray); + this.setExtendedState(JFrame.MAXIMIZED_BOTH); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (MainWindow.this.getExtendedState() != JFrame.MAXIMIZED_BOTH) { + windowPosition.setFullScreen(false); + if (windowPosition.isSavedWindowPositionValid()) { + MainWindow.this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), + windowPosition.getWindowWidth(), windowPosition.getWindowHeight()); + } + MainWindow.this.removeComponentListener(this); + } + } + }); + + } else { + this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), windowPosition.getWindowWidth(), + windowPosition.getWindowHeight()); + } + } + + private void setHideFindBoxOnMainWindowFocus() { + this.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + if (findBox != null && findBox.isVisible()) { + findBox.setVisible(false); + } + } + }); + } + + private void setShowFindAllBoxOnMainWindowFocus() { + this.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + if (findAllBox != null && findAllBox.isVisible()) { + findAllBox.setVisible(false); + } + } + }); + } + + private void setQuitOnWindowClosing() { + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + quit(); + } + }); + } + + private void quit() { + try { + windowPosition.readPositionFromWindow(this); + configSaver.saveConfig(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } finally { + try { + this.dispose(); + } finally { + System.exit(0); + } + } + } + + private void setExitOnEscWhenEnabled(JComponent mainComponent) { + Action escapeAction = new AbstractAction() { + private static final long serialVersionUID = -3460391555954575248L; + + @Override + public void actionPerformed(ActionEvent e) { + if (luytenPrefs.isExitByEscEnabled()) { + quit(); + } + } + }; + KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); + mainComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeKeyStroke, "ESCAPE"); + mainComponent.getActionMap().put("ESCAPE", escapeAction); + } + + public Model getSelectedModel() { + return (Model) jarsTabbedPane.getSelectedComponent(); + } + + public Collection getModels() { + return jarModels.values(); + } + + public JProgressBar getBar() { + return bar; + } + + public JLabel getLabel() { + return label; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Model.java b/src/main/java/us/deathmarine/luyten/Model.java new file mode 100644 index 00000000..1eca0cd2 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Model.java @@ -0,0 +1,1031 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.InputTypeLoader; +import com.strobel.assembler.metadata.ITypeLoader; +import com.strobel.assembler.metadata.JarTypeLoader; +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.core.VerifyArgument; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTree; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rtextarea.RTextScrollPane; + +/** + * Jar-level model + */ +public class Model extends JSplitPane { + + private static final long serialVersionUID = 6896857630400910200L; + + private static final long MAX_JAR_FILE_SIZE_BYTES = 10_000_000_000L; + private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; + + private final LuytenTypeLoader typeLoader = new LuytenTypeLoader(); + private final MetadataSystem metadataSystem = new MetadataSystem(typeLoader); + + private final JTree tree; + public JTabbedPane house; + private File file; + private final DecompilerSettings settings; + private final DecompilationOptions decompilationOptions; + private Theme theme; + private final MainWindow mainWindow; + private final JProgressBar bar; + private JLabel label; + private final HashSet hmap = new HashSet<>(); + private Set treeExpansionState; + private boolean open = false; + private State state; + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; + + public Model(MainWindow mainWindow) { + this.mainWindow = mainWindow; + this.bar = mainWindow.getBar(); + this.setLabel(mainWindow.getLabel()); + + configSaver = ConfigSaver.getLoadedInstance(); + settings = configSaver.getDecompilerSettings(); + luytenPrefs = configSaver.getLuytenPreferences(); + + try { + String themeXml = luytenPrefs.getThemeXml(); + setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); + } catch (Exception e1) { + try { + Luyten.showExceptionDialog("Exception!", e1); + String themeXml = LuytenPreferences.DEFAULT_THEME_XML; + luytenPrefs.setThemeXml(themeXml); + setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); + } catch (Exception e2) { + Luyten.showExceptionDialog("Exception!", e2); + } + } + + tree = new JTree(); + tree.setModel(new DefaultTreeModel(null)); + tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.setCellRenderer(new CellRenderer()); + TreeListener tl = new TreeListener(); + tree.addMouseListener(tl); + tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener()); + tree.addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + openEntryByTreePath(tree.getSelectionPath()); + } + } + }); + + JPanel panel2 = new JPanel(); + panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS)); + panel2.setBorder(BorderFactory.createTitledBorder("Structure")); + panel2.add(new JScrollPane(tree)); + + house = new JTabbedPane(); + house.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + house.addChangeListener(new TabChangeListener()); + house.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isMiddleMouseButton(e)) { + closeOpenTab(house.getSelectedIndex()); + } + } + }); + + KeyStroke sfuncF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, Keymap.ctrlDownModifier(), false); + mainWindow.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF4, "CloseTab"); + + mainWindow.getRootPane().getActionMap().put("CloseTab", new AbstractAction() { + private static final long serialVersionUID = -885398399200419492L; + + @Override + public void actionPerformed(ActionEvent e) { + closeOpenTab(house.getSelectedIndex()); + } + + }); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createTitledBorder("Code")); + panel.add(house); + this.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + this.setDividerLocation(250 % mainWindow.getWidth()); + this.setLeftComponent(panel2); + this.setRightComponent(panel); + + decompilationOptions = new DecompilationOptions(); + decompilationOptions.setSettings(settings); + decompilationOptions.setFullDecompilation(true); + } + + public void showLegal(String legalStr) { + show("Legal", legalStr); + } + + public void show(String name, String contents) { + OpenFile open = new OpenFile(name, "*/" + name, getTheme(), mainWindow); + open.setContent(contents); + hmap.add(open); + addOrSwitchToTab(open); + } + + private void addOrSwitchToTab(final OpenFile open) { + SwingUtilities.invokeLater(() -> { + try { + final String title = open.name; + RTextScrollPane rTextScrollPane = open.scrollPane; + int index = house.indexOfTab(title); + if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { + index = -1; + for (int i = 0; i < house.getTabCount(); i++) { + if (house.getComponentAt(i) == open.scrollPane) { + index = i; + break; + } + } + } + if (index < 0) { + house.addTab(title, rTextScrollPane); + index = house.indexOfComponent(rTextScrollPane); + house.setSelectedIndex(index); + Tab ct = new Tab(title, () -> { + int index1 = house.indexOfTab(title); + closeOpenTab(index1); + }); + house.setTabComponentAt(index, ct); + } else { + house.setSelectedIndex(index); + } + open.onAddedToScreen(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); + } + + public void closeOpenTab(int index) { + if (index < 0 || index >= house.getComponentCount()) + return; + + RTextScrollPane co = (RTextScrollPane) house.getComponentAt(index); + RSyntaxTextArea pane = (RSyntaxTextArea) co.getViewport().getView(); + OpenFile open = null; + for (OpenFile file : hmap) + if (pane.equals(file.textArea)) + open = file; + if (open != null) + hmap.remove(open); + house.remove(co); + if (open != null) + open.close(); + } + + private String getName(String path) { + if (path == null) + return ""; + int i = path.lastIndexOf("/"); + if (i == -1) + i = path.lastIndexOf("\\"); + if (i != -1) + return path.substring(i + 1); + return path; + } + + private class TreeListener extends MouseAdapter { + @Override + public void mousePressed(MouseEvent event) { + boolean isClickCountMatches = (event.getClickCount() == 1 && luytenPrefs.isSingleClickOpenEnabled()) + || (event.getClickCount() == 2 && !luytenPrefs.isSingleClickOpenEnabled()); + if (!isClickCountMatches) + return; + + if (!SwingUtilities.isLeftMouseButton(event)) + return; + + final TreePath trp = tree.getPathForLocation(event.getX(), event.getY()); + if (trp == null) + return; + + Object lastPathComponent = trp.getLastPathComponent(); + boolean isLeaf = (lastPathComponent instanceof TreeNode && ((TreeNode) lastPathComponent).isLeaf()); + if (!isLeaf) + return; + + new Thread(() -> openEntryByTreePath(trp)).start(); + } + } + + private class FurtherExpandingTreeExpansionListener implements TreeExpansionListener { + @Override + public void treeExpanded(final TreeExpansionEvent event) { + final TreePath treePath = event.getPath(); + + final Object expandedTreePathObject = treePath.getLastPathComponent(); + if (!(expandedTreePathObject instanceof TreeNode)) { + return; + } + + final TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject; + if (expandedTreeNode.getChildCount() == 1) { + final TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0); + + if (descendantTreeNode.isLeaf()) { + return; + } + + final TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode); + tree.expandPath(nextTreePath); + } + } + + @Override + public void treeCollapsed(final TreeExpansionEvent event) { + + } + } + + public void openEntryByTreePath(TreePath trp) { + String name = ""; + StringBuilder path = new StringBuilder(); + try { + bar.setVisible(true); + if (trp.getPathCount() > 1) { + for (int i = 1; i < trp.getPathCount(); i++) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); + TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); + if (i == trp.getPathCount() - 1) { + name = userObject.getOriginalName(); + } else { + path.append(userObject.getOriginalName()).append("/"); + } + } + path.append(name); + + if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) { + if (state == null) { + JarFile jfile = new JarFile(file); + ITypeLoader jarLoader = new JarTypeLoader(jfile); + + typeLoader.getTypeLoaders().add(jarLoader); + state = new State(file.getCanonicalPath(), file, jfile, jarLoader); + } + + JarEntry entry = state.jarFile.getJarEntry(path.toString()); + if (entry == null) { + throw new FileEntryNotFoundException(); + } + if (entry.getSize() > MAX_UNPACKED_FILE_SIZE_BYTES) { + throw new TooLargeFileException(entry.getSize()); + } + String entryName = entry.getName(); + if (entryName.endsWith(".class")) { + getLabel().setText("Extracting: " + name); + String internalName = StringUtilities.removeRight(entryName, ".class"); + TypeReference type = metadataSystem.lookupType(internalName); + extractClassToTextPane(type, name, path.toString(), null); + } else { + getLabel().setText("Opening: " + name); + try (InputStream in = state.jarFile.getInputStream(entry)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); + } + } + } + } else { + name = file.getName(); + path = new StringBuilder(file.getPath().replaceAll("\\\\", "/")); + if (file.length() > MAX_UNPACKED_FILE_SIZE_BYTES) { + throw new TooLargeFileException(file.length()); + } + if (name.endsWith(".class")) { + getLabel().setText("Extracting: " + name); + TypeReference type = metadataSystem.lookupType(path.toString()); + extractClassToTextPane(type, name, path.toString(), null); + } else { + getLabel().setText("Opening: " + name); + try (InputStream in = new FileInputStream(file)) { + extractSimpleFileEntryToTextPane(in, name, path.toString()); + } + } + } + + getLabel().setText("Complete"); + } catch (FileEntryNotFoundException e) { + getLabel().setText("File not found: " + name); + } catch (FileIsBinaryException e) { + getLabel().setText("Binary resource: " + name); + } catch (TooLargeFileException e) { + getLabel().setText("File is too large: " + name + " - size: " + e.getReadableFileSize()); + } catch (Exception e) { + getLabel().setText("Cannot open: " + name); + Luyten.showExceptionDialog("Unable to open file!", e); + } finally { + bar.setVisible(false); + } + } + + void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigationLink) + throws Exception { + if (tabTitle == null || tabTitle.trim().length() < 1 || path == null) { + throw new FileEntryNotFoundException(); + } + OpenFile sameTitledOpen = null; + for (OpenFile nextOpen : hmap) { + if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path) && type.equals(nextOpen.getType())) { + sameTitledOpen = nextOpen; + break; + } + } + if (sameTitledOpen != null && sameTitledOpen.isContentValid()) { + sameTitledOpen.setInitialNavigationLink(navigationLink); + addOrSwitchToTab(sameTitledOpen); + return; + } + + // resolve TypeDefinition + TypeDefinition resolvedType; + if (type == null || ((resolvedType = type.resolve()) == null)) { + throw new Exception("Unable to resolve type."); + } + + // open tab, store type information, start decompilation + if (sameTitledOpen != null) { + sameTitledOpen.path = path; + sameTitledOpen.invalidateContent(); + sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + sameTitledOpen.setType(resolvedType); + sameTitledOpen.setInitialNavigationLink(navigationLink); + sameTitledOpen.resetScrollPosition(); + sameTitledOpen.decompile(); + addOrSwitchToTab(sameTitledOpen); + } else { + OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); + open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + open.setType(resolvedType); + open.setInitialNavigationLink(navigationLink); + open.decompile(); + hmap.add(open); + addOrSwitchToTab(open); + } + } + + public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tabTitle, String path) + throws Exception { + if (inputStream == null || tabTitle == null || tabTitle.trim().length() < 1 || path == null) { + throw new FileEntryNotFoundException(); + } + OpenFile sameTitledOpen = null; + for (OpenFile nextOpen : hmap) { + if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path)) { + sameTitledOpen = nextOpen; + break; + } + } + if (sameTitledOpen != null) { + addOrSwitchToTab(sameTitledOpen); + return; + } + + // build tab content and check if file is binary + double ascii = 0; + double other = 0; + StringBuilder sb = new StringBuilder(); + try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader reader = new BufferedReader(inputStreamReader)) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + // Source: https://stackoverflow.com/a/13533390/5894824 + for (byte b : line.getBytes()) { + if (b == 0x09 || b == 0x0A || b == 0x0C || b == 0x0D || (b >= 0x20 && b <= 0x7E)) ascii++; + else other++; + } + } + } + + if (other != 0 && other / (ascii + other) > 0.5) { + throw new FileIsBinaryException(); + } + + // open tab + if (sameTitledOpen != null) { + sameTitledOpen.path = path; + sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + sameTitledOpen.resetScrollPosition(); + sameTitledOpen.setContent(sb.toString()); + addOrSwitchToTab(sameTitledOpen); + } else { + OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); + open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + open.setContent(sb.toString()); + hmap.add(open); + addOrSwitchToTab(open); + } + } + + private class TabChangeListener implements ChangeListener { + @Override + public void stateChanged(ChangeEvent e) { + int selectedIndex = house.getSelectedIndex(); + if (selectedIndex < 0) { + return; + } + for (OpenFile open : hmap) { + if (house.indexOfTab(open.name) == selectedIndex) { + + if (open.getType() != null && !open.isContentValid()) { + updateOpenClass(open); + break; + } + + } + } + } + } + + public void updateOpenClasses() { + // invalidate all open classes (update will hapen at tab change) + for (OpenFile open : hmap) { + if (open.getType() != null) { + open.invalidateContent(); + } + } + // update the current open tab - if it is a class + for (OpenFile open : hmap) { + if (open.getType() != null && isTabInForeground(open)) { + updateOpenClass(open); + break; + } + } + } + + private void updateOpenClass(final OpenFile open) { + if (open.getType() == null) { + return; + } + new Thread(() -> { + try { + bar.setVisible(true); + getLabel().setText("Extracting: " + open.name); + open.invalidateContent(); + open.decompile(); + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Error, cannot update: " + open.name); + } finally { + bar.setVisible(false); + } + }).start(); + } + + private boolean isTabInForeground(OpenFile open) { + String title = open.name; + int selectedIndex = house.getSelectedIndex(); + return (selectedIndex >= 0 && selectedIndex == house.indexOfTab(title)); + } + + final class State implements AutoCloseable { + private final String key; + private final File file; + final JarFile jarFile; + final ITypeLoader typeLoader; + + private State(String key, File file, JarFile jarFile, ITypeLoader typeLoader) { + this.key = VerifyArgument.notNull(key, "key"); + this.file = VerifyArgument.notNull(file, "file"); + this.jarFile = jarFile; + this.typeLoader = typeLoader; + } + + @Override + public void close() { + if (typeLoader != null) { + Model.this.typeLoader.getTypeLoaders().remove(typeLoader); + } + Closer.tryClose(jarFile); + } + + public File getFile() { + return file; + } + + public String getKey() { + return key; + } + } + + public static class Tab extends JPanel { + private final JLabel tabTitle; + private final JLabel closeButton = new JLabel(new ImageIcon( + Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/icon_close.png")))); + + public Tab(String title, final Runnable onCloseTabAction) { + super(new GridBagLayout()); + this.setOpaque(false); + this.tabTitle = new JLabel(title); + this.createTab(); + + // TODO: Disables tab switching... Is there a workaround? + /*addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (onCloseTabAction != null && SwingUtilities.isMiddleMouseButton(e)) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + } + });*/ + + closeButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (onCloseTabAction != null) { + try { + onCloseTabAction.run(); + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + } + }); + } + + public void createTab() { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1; + this.add(tabTitle, gbc); + gbc.gridx++; + gbc.insets = new Insets(0, 5, 0, 0); + gbc.anchor = GridBagConstraints.EAST; + this.add(closeButton, gbc); + } + } + + public DefaultMutableTreeNode loadNodesByNames(DefaultMutableTreeNode node, List originalNames) { + List args = new ArrayList<>(); + for (String originalName : originalNames) { + args.add(new TreeNodeUserObject(originalName)); + } + return loadNodesByUserObj(node, args); + } + + public DefaultMutableTreeNode loadNodesByUserObj(DefaultMutableTreeNode node, List args) { + if (args.size() > 0) { + TreeNodeUserObject name = args.remove(0); + DefaultMutableTreeNode nod = getChild(node, name); + if (nod == null) + nod = new DefaultMutableTreeNode(name); + node.add(loadNodesByUserObj(nod, args)); + } + return node; + } + + @SuppressWarnings("unchecked") + public DefaultMutableTreeNode getChild(DefaultMutableTreeNode node, TreeNodeUserObject name) { + Enumeration entry = node.children(); + while (entry.hasMoreElements()) { + DefaultMutableTreeNode nods = (DefaultMutableTreeNode) entry.nextElement(); + if (((TreeNodeUserObject) nods.getUserObject()).getOriginalName().equals(name.getOriginalName())) { + return nods; + } + } + return null; + } + + public void loadFile(File file) { + if (open) + closeFile(); + this.file = file; + + RecentFiles.add(file.getAbsolutePath()); + mainWindow.mainMenuBar.updateRecentFiles(); + loadTree(); + } + + public void updateTree() { + TreeUtil treeUtil = new TreeUtil(tree); + treeExpansionState = treeUtil.getExpansionState(); + loadTree(); + } + + public void loadTree() { + new Thread(() -> { + try { + if (file == null) { + return; + } + tree.setModel(new DefaultTreeModel(null)); + + if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { + throw new TooLargeFileException(file.length()); + } + if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { + JarFile jfile = new JarFile(file); + getLabel().setText("Loading: " + jfile.getName()); + bar.setVisible(true); + + JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); + List mass; + if (luytenPrefs.isFilterOutInnerClassEntries()) { + mass = jarEntryFilter.getEntriesWithoutInnerClasses(); + } else { + mass = jarEntryFilter.getAllEntriesFromJar(); + } + buildTreeFromMass(mass); + + if (state == null) { + ITypeLoader jarLoader = new JarTypeLoader(jfile); + typeLoader.getTypeLoaders().add(jarLoader); + state = new State(file.getCanonicalPath(), file, jfile, jarLoader); + } + open = true; + getLabel().setText("Complete"); + } else { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + tree.setModel(new DefaultTreeModel(top)); + settings.setTypeLoader(new InputTypeLoader()); + open = true; + getLabel().setText("Complete"); + + // open it automatically + new Thread(() -> { + TreePath trp = new TreePath(top.getPath()); + openEntryByTreePath(trp); + }).start(); + } + + if (treeExpansionState != null) { + try { + TreeUtil treeUtil = new TreeUtil(tree); + treeUtil.restoreExpanstionState(treeExpansionState); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + } catch (TooLargeFileException e) { + getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); + closeFile(); + } catch (Exception e1) { + Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); + getLabel().setText("Cannot open: " + file.getName()); + closeFile(); + } finally { + mainWindow.onFileLoadEnded(); + bar.setVisible(false); + } + }).start(); + } + + private void buildTreeFromMass(List mass) { + if (luytenPrefs.isPackageExplorerStyle()) { + buildFlatTreeFromMass(mass); + } else { + buildDirectoryTreeFromMass(mass); + } + } + + private void buildDirectoryTreeFromMass(List mass) { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + List sort = new ArrayList<>(); + mass.sort(String.CASE_INSENSITIVE_ORDER); + for (String m : mass) + if (m.contains("META-INF") && !sort.contains(m)) + sort.add(m); + Set set = new HashSet<>(); + for (String m : mass) { + if (m.contains("/")) { + set.add(m.substring(0, m.lastIndexOf("/") + 1)); + } + } + List packs = Arrays.asList(set.toArray(new String[]{})); + packs.sort(String.CASE_INSENSITIVE_ORDER); + packs.sort((o1, o2) -> o2.split("/").length - o1.split("/").length); + for (String pack : packs) + for (String m : mass) + if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) + sort.add(m); + for (String m : mass) + if (!m.contains("META-INF") && !m.contains("/") && !sort.contains(m)) + sort.add(m); + for (String pack : sort) { + LinkedList list = new LinkedList<>(Arrays.asList(pack.split("/"))); + loadNodesByNames(top, list); + } + tree.setModel(new DefaultTreeModel(top)); + } + + private void buildFlatTreeFromMass(List mass) { + TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); + DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); + + TreeMap> packages = new TreeMap<>(); + HashSet classContainingPackageRoots = new HashSet<>(); + + // (assertion: mass does not contain null elements) + Comparator sortByFileExtensionsComparator = Comparator.comparing( + (String o) -> o.replaceAll("[^.]*\\.", "")).thenComparing(o -> o); + + for (String entry : mass) { + String packagePath = ""; + String packageRoot = ""; + if (entry.contains("/")) { + packagePath = entry.replaceAll("/[^/]*$", ""); + packageRoot = entry.replaceAll("/.*$", ""); + } + String packageEntry = entry.replace(packagePath + "/", ""); + if (!packages.containsKey(packagePath)) { + packages.put(packagePath, new TreeSet<>(sortByFileExtensionsComparator)); + } + packages.get(packagePath).add(packageEntry); + if (!entry.startsWith("META-INF") && packageRoot.trim().length() > 0 + && entry.matches(".*\\.(class|java|prop|properties)$")) { + classContainingPackageRoots.add(packageRoot); + } + } + + // META-INF comes first -> not flat + for (String packagePath : packages.keySet()) { + if (packagePath.startsWith("META-INF")) { + List packagePathElements = Arrays.asList(packagePath.split("/")); + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(packagePathElements); + list.add(entry); + loadNodesByNames(top, list); + } + } + } + + // real packages: path starts with a classContainingPackageRoot -> flat + for (String packagePath : packages.keySet()) { + String packageRoot = packagePath.replaceAll("/.*$", ""); + if (classContainingPackageRoots.contains(packageRoot)) { + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(); + list.add(new TreeNodeUserObject(packagePath, packagePath.replaceAll("/", "."))); + list.add(new TreeNodeUserObject(entry)); + loadNodesByUserObj(top, list); + } + } + } + + // the rest, not real packages but directories -> not flat + for (String packagePath : packages.keySet()) { + String packageRoot = packagePath.replaceAll("/.*$", ""); + if (!classContainingPackageRoots.contains(packageRoot) && !packagePath.startsWith("META-INF") + && packagePath.length() > 0) { + List packagePathElements = Arrays.asList(packagePath.split("/")); + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(packagePathElements); + list.add(entry); + loadNodesByNames(top, list); + } + } + } + + // the default package -> not flat + String packagePath = ""; + if (packages.containsKey(packagePath)) { + for (String entry : packages.get(packagePath)) { + ArrayList list = new ArrayList<>(); + list.add(entry); + loadNodesByNames(top, list); + } + } + tree.setModel(new DefaultTreeModel(top)); + } + + public void closeFile() { + for (OpenFile co : hmap) { + int pos = house.indexOfTab(co.name); + if (pos >= 0) + house.remove(pos); + co.close(); + } + + if (state != null) { + Closer.tryClose(state); + } + state = null; + + hmap.clear(); + tree.setModel(new DefaultTreeModel(null)); + file = null; + treeExpansionState = null; + open = false; + mainWindow.onFileLoadEnded(); + } + + public void changeTheme(String xml) { + InputStream in = getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + xml); + try { + if (in != null) { + setTheme(Theme.load(in)); + for (OpenFile f : hmap) { + getTheme().apply(f.textArea); + } + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + } + + public File getOpenedFile() { + File openedFile = null; + if (file != null && open) { + openedFile = file; + } + if (openedFile == null) { + getLabel().setText("No open file"); + } + return openedFile; + } + + public String getCurrentTabTitle() { + String tabTitle = null; + try { + int pos = house.getSelectedIndex(); + if (pos >= 0) { + tabTitle = house.getTitleAt(pos); + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + if (tabTitle == null) { + getLabel().setText("No open tab"); + } + return tabTitle; + } + + public RSyntaxTextArea getCurrentTextArea() { + RSyntaxTextArea currentTextArea = null; + try { + int pos = house.getSelectedIndex(); + System.out.println(pos); + if (pos >= 0) { + RTextScrollPane co = (RTextScrollPane) house.getComponentAt(pos); + currentTextArea = (RSyntaxTextArea) co.getViewport().getView(); + } + } catch (Exception e1) { + Luyten.showExceptionDialog("Exception!", e1); + } + if (currentTextArea == null) { + getLabel().setText("No open tab"); + } + return currentTextArea; + } + + public void startWarmUpThread() { + new Thread(() -> { + try { + Thread.sleep(500); + String internalName = FindBox.class.getName(); + TypeReference type = metadataSystem.lookupType(internalName); + TypeDefinition resolvedType; + if ((type == null) || ((resolvedType = type.resolve()) == null)) { + return; + } + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput + .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); + String decompiledSource = stringwriter.toString(); + OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); + open.setContent(decompiledSource); + JTabbedPane pane = new JTabbedPane(); + pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + pane.addTab("title", open.scrollPane); + pane.setSelectedIndex(pane.indexOfTab("title")); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }).start(); + } + + public void navigateTo(final String uniqueStr) { + new Thread(() -> { + if (uniqueStr == null) + return; + String[] linkParts = uniqueStr.split("\\|"); + if (linkParts.length <= 1) + return; + String destinationTypeStr = linkParts[1]; + try { + bar.setVisible(true); + getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); + + TypeReference type = metadataSystem.lookupType(destinationTypeStr); + if (type == null) + throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); + TypeDefinition typeDef = type.resolve(); + if (typeDef == null) + throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); + + String tabTitle = typeDef.getName() + ".class"; + extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); + + getLabel().setText("Complete"); + } catch (Exception e) { + getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); + Luyten.showExceptionDialog("Cannot Navigate!", e); + } finally { + bar.setVisible(false); + } + }).start(); + } + + public JLabel getLabel() { + return label; + } + + public void setLabel(JLabel label) { + this.label = label; + } + + public State getState() { + return state; + } + + public Theme getTheme() { + return theme; + } + + public void setTheme(Theme theme) { + this.theme = theme; + } + + public MetadataSystem getMetadataSystem() { + return metadataSystem; + } + + public String getFileName() { + return file == null ? null : getName(file.getName()); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/OpenFile.java b/src/main/java/us/deathmarine/luyten/OpenFile.java new file mode 100644 index 00000000..3a13db12 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/OpenFile.java @@ -0,0 +1,747 @@ +package us.deathmarine.luyten; + +import com.strobel.assembler.metadata.MetadataSystem; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.decompiler.DecompilationOptions; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import com.strobel.decompiler.languages.Languages; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.io.StringWriter; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JScrollBar; +import javax.swing.JViewport; +import javax.swing.ScrollPaneConstants; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.event.HyperlinkEvent; +import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rtextarea.RTextScrollPane; + +public class OpenFile { + + // navigation links + private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); + private final Map isNavigableCache = new ConcurrentHashMap<>(); + private final Map readableLinksCache = new ConcurrentHashMap<>(); + + private volatile boolean isContentValid = false; + private volatile boolean isNavigationLinksValid = false; + private volatile boolean isWaitForLinksCursor = false; + private volatile Double lastScrollPercent = null; + + private LinkProvider linkProvider; + private String initialNavigationLink; + private boolean isFirstTimeRun = true; + + MainWindow mainWindow; + RTextScrollPane scrollPane; + RSyntaxTextArea textArea; + String name; + String path; + + private final ConfigSaver configSaver; + private final LuytenPreferences luytenPrefs; + + // decompiler and type references (not needed for text files) + private MetadataSystem metadataSystem; + private DecompilerSettings settings; + private DecompilationOptions decompilationOptions; + private TypeDefinition type; + + public OpenFile(String name, String path, Theme theme, final MainWindow mainWindow) { + this.name = name; + this.path = path; + this.mainWindow = mainWindow; + + configSaver = ConfigSaver.getLoadedInstance(); + luytenPrefs = configSaver.getLuytenPreferences(); + + textArea = new RSyntaxTextArea(25, 70); + textArea.setCaretPosition(0); + textArea.requestFocusInWindow(); + textArea.setMarkOccurrences(true); + textArea.setClearWhitespaceLinesEnabled(false); + textArea.setEditable(false); + textArea.setAntiAliasingEnabled(true); + textArea.setCodeFoldingEnabled(true); + + FileUtil.setLanguage(textArea, name); + scrollPane = new RTextScrollPane(textArea, true); + + scrollPane.setIconRowHeaderEnabled(true); + textArea.setText(""); + + // Edit RTextArea's PopupMenu + JPopupMenu pop = textArea.getPopupMenu(); + pop.addSeparator(); + JMenuItem item = new JMenuItem("Font"); + item.addActionListener(e -> { + JFontChooser fontChooser = new JFontChooser(); + fontChooser.setSelectedFont(textArea.getFont()); + fontChooser.setSelectedFontSize(textArea.getFont().getSize()); + int result = fontChooser.showDialog(mainWindow); + if (result == JFontChooser.OK_OPTION) { + textArea.setFont(fontChooser.getSelectedFont()); + luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); + } + }); + pop.add(item); + textArea.setPopupMenu(pop); + + theme.apply(textArea); + + textArea.setFont(new Font(textArea.getFont().getName(), textArea.getFont().getStyle(), + luytenPrefs.getFont_size())); + + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + final JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); + if (verticalScrollbar != null) { + verticalScrollbar.addAdjustmentListener(e -> { + String content = textArea.getText(); + if (content == null || content.length() == 0) + return; + int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) + return; + lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); + }); + } + + textArea.setHyperlinksEnabled(true); + textArea.setLinkScanningMask(Keymap.ctrlDownModifier()); + + textArea.setLinkGenerator((textArea, offs) -> { + final String uniqueStr = getUniqueStrForOffset(offs); + final Integer selectionFrom = getSelectionFromForOffset(offs); + if (uniqueStr != null && selectionFrom != null) { + return new LinkGeneratorResult() { + @Override + public HyperlinkEvent execute() { + if (isNavigationLinksValid) + onNavigationClicked(uniqueStr); + return null; + } + + @Override + public int getSourceOffset() { + if (isNavigationLinksValid) + return selectionFrom; + return offs; + } + }; + } + return null; + }); + + /* + * Add Ctrl+Wheel Zoom for Text Size Removes all standard listeners and + * writes new listeners for wheelscroll movement. + */ + for (MouseWheelListener listeners : scrollPane.getMouseWheelListeners()) { + scrollPane.removeMouseWheelListener(listeners); + } + + scrollPane.addMouseWheelListener(e -> { + if (e.getWheelRotation() == 0) { + // Nothing to do here. This happens when scroll event is delivered from a touchbar + // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no + // trivial and consistent way to use that + // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 + return; + } + + if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { + Font font = textArea.getFont(); + int size = font.getSize(); + if (e.getWheelRotation() > 0) { + textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); + } else { + textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); + } + luytenPrefs.setFont_size(size); + } else { + if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { + JScrollBar toScroll = scrollPane.getVerticalScrollBar(); + int direction = e.getWheelRotation() < 0 ? -1 : 1; + int orientation = SwingConstants.VERTICAL; + if (toScroll == null || !toScroll.isVisible()) { + toScroll = scrollPane.getHorizontalScrollBar(); + if (toScroll == null || !toScroll.isVisible()) { + return; + } + orientation = SwingConstants.HORIZONTAL; + } + e.consume(); + + if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { + JViewport vp = scrollPane.getViewport(); + if (vp == null) { + return; + } + Component comp = vp.getView(); + int units = Math.abs(e.getUnitsToScroll()); + boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; + Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); + if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { + Scrollable scrollComp = (Scrollable) comp; + Rectangle viewRect = vp.getViewRect(); + int startingX = viewRect.x; + boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); + int scrollMin = toScroll.getMinimum(); + int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); + + if (limitScroll) { + int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, + direction); + if (direction < 0) { + scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); + } else { + scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); + } + } + + for (int i = 0; i < units; i++) { + int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, + direction); + if (orientation == SwingConstants.VERTICAL) { + if (direction < 0) { + viewRect.y -= unitIncr; + if (viewRect.y <= scrollMin) { + viewRect.y = scrollMin; + break; + } + } else { // (direction > 0 + viewRect.y += unitIncr; + if (viewRect.y >= scrollMax) { + viewRect.y = scrollMax; + break; + } + } + } else { + if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { + viewRect.x -= unitIncr; + if (leftToRight) { + if (viewRect.x < scrollMin) { + viewRect.x = scrollMin; + break; + } + } + } else { + viewRect.x += unitIncr; + if (leftToRight) { + if (viewRect.x > scrollMax) { + viewRect.x = scrollMax; + break; + } + } + } + } + } + if (orientation == SwingConstants.VERTICAL) { + toScroll.setValue(viewRect.y); + } else { + if (leftToRight) { + toScroll.setValue(viewRect.x); + } else { + int newPos = toScroll.getValue() - (viewRect.x - startingX); + if (newPos < scrollMin) { + newPos = scrollMin; + } else if (newPos > scrollMax) { + newPos = scrollMax; + } + toScroll.setValue(newPos); + } + } + } else { + int delta; + int limit = -1; + + if (limitScroll) { + if (direction < 0) { + limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); + } else { + limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); + } + } + + for (int i = 0; i < units; i++) { + if (direction > 0) { + delta = toScroll.getUnitIncrement(direction); + } else { + delta = -toScroll.getUnitIncrement(direction); + } + int oldValue = toScroll.getValue(); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + if (oldValue == newValue) { + break; + } + if (limitScroll && i > 0) { + assert limit != -1; + if ((direction < 0 && newValue < limit) + || (direction > 0 && newValue > limit)) { + break; + } + } + toScroll.setValue(newValue); + } + + } + } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { + int oldValue = toScroll.getValue(); + int blockIncrement = toScroll.getBlockIncrement(direction); + int delta = blockIncrement * ((direction > 0) ? +1 : -1); + int newValue = oldValue + delta; + if (delta > 0 && newValue < oldValue) { + newValue = toScroll.getMaximum(); + } else if (delta < 0 && newValue > oldValue) { + newValue = toScroll.getMinimum(); + } + toScroll.setValue(newValue); + } + } + } + + e.consume(); + }); + + textArea.addMouseMotionListener(new MouseMotionAdapter() { + private boolean isLinkLabelPrev = false; + private String prevLinkText = null; + + @Override + public synchronized void mouseMoved(MouseEvent e) { + String linkText = null; + boolean isLinkLabel = false; + boolean isCtrlDown = (e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0; + if (isCtrlDown) { + linkText = createLinkLabel(e); + isLinkLabel = linkText != null; + } + if (isCtrlDown && isWaitForLinksCursor) { + textArea.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + } else if (textArea.getCursor().getType() == Cursor.WAIT_CURSOR) { + textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + + JLabel label = OpenFile.this.mainWindow.getLabel(); + + if (isLinkLabel && isLinkLabelPrev) { + if (!linkText.equals(prevLinkText)) { + setLinkLabel(label, linkText); + } + } else if (isLinkLabel) { + setLinkLabel(label, linkText); + + } else if (isLinkLabelPrev) { + setLinkLabel(label, null); + } + isLinkLabelPrev = isLinkLabel; + prevLinkText = linkText; + } + + private void setLinkLabel(JLabel label, String text) { + String current = label.getText(); + if (text == null && current != null) + if (current.startsWith("Navigating:") || current.startsWith("Cannot navigate:")) + return; + label.setText(text != null ? text : "Complete"); + } + + private String createLinkLabel(MouseEvent e) { + int offs = textArea.viewToModel(e.getPoint()); + if (isNavigationLinksValid) { + return getLinkDescriptionForOffset(offs); + } + return null; + } + }); + } + + public void setContent(String content) { + textArea.setText(content); + FileUtil.setLanguage(textArea, name); + } + + public void decompile() { + this.invalidateContent(); + // synchronized: do not accept changes from menu while running + synchronized (settings) { + if (Languages.java().getName().equals(settings.getLanguage().getName())) { + decompileWithNavigationLinks(); + } else { + decompileWithoutLinks(); + } + } + } + + private void decompileWithoutLinks() { + this.invalidateContent(); + isNavigationLinksValid = false; + textArea.setHyperlinksEnabled(false); + + StringWriter stringwriter = new StringWriter(); + PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); + plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); + settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); + setContentPreserveLastScrollPosition(stringwriter.toString()); + this.isContentValid = true; + } + + private void decompileWithNavigationLinks() { + this.invalidateContent(); + DecompilerLinkProvider newLinkProvider = new DecompilerLinkProvider(); + newLinkProvider.setDecompilerReferences(metadataSystem, settings, decompilationOptions); + newLinkProvider.setType(type); + linkProvider = newLinkProvider; + + linkProvider.generateContent(); + setContentPreserveLastScrollPosition(linkProvider.getTextContent()); + this.isContentValid = true; + enableLinks(); + } + + private void setContentPreserveLastScrollPosition(final String content) { + final Double scrollPercent = lastScrollPercent; + if (scrollPercent != null && initialNavigationLink == null) { + SwingUtilities.invokeLater(() -> { + textArea.setText(content); + restoreScrollPosition(scrollPercent); + }); + } else { + textArea.setText(content); + } + } + + private void restoreScrollPosition(final double position) { + SwingUtilities.invokeLater(() -> { + JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); + if (verticalScrollbar == null) + return; + int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); + long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); + if (newScrollValue < verticalScrollbar.getMinimum()) + newScrollValue = verticalScrollbar.getMinimum(); + if (newScrollValue > verticalScrollbar.getMaximum()) + newScrollValue = verticalScrollbar.getMaximum(); + verticalScrollbar.setValue((int) newScrollValue); + }); + } + + private void enableLinks() { + if (initialNavigationLink != null) { + doEnableLinks(); + } else { + new Thread(() -> { + try { + isWaitForLinksCursor = true; + doEnableLinks(); + } finally { + isWaitForLinksCursor = false; + resetCursor(); + } + }).start(); + } + } + + private void resetCursor() { + SwingUtilities.invokeLater(() -> textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR))); + } + + private void doEnableLinks() { + isNavigationLinksValid = false; + linkProvider.processLinks(); + buildSelectionToUniqueStrTreeMap(); + clearLinksCache(); + isNavigationLinksValid = true; + textArea.setHyperlinksEnabled(true); + warmUpWithFirstLink(); + } + + private void warmUpWithFirstLink() { + if (selectionToUniqueStrTreeMap.keySet().size() > 0) { + Selection selection = selectionToUniqueStrTreeMap.keySet().iterator().next(); + getLinkDescriptionForOffset(selection.from); + } + } + + public void clearLinksCache() { + try { + isNavigableCache.clear(); + readableLinksCache.clear(); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void buildSelectionToUniqueStrTreeMap() { + TreeMap treeMap = new TreeMap<>(); + Map definitionToSelectionMap = linkProvider.getDefinitionToSelectionMap(); + Map> referenceToSelectionsMap = linkProvider.getReferenceToSelectionsMap(); + + for (String key : definitionToSelectionMap.keySet()) { + Selection selection = definitionToSelectionMap.get(key); + treeMap.put(selection, key); + } + for (String key : referenceToSelectionsMap.keySet()) { + for (Selection selection : referenceToSelectionsMap.get(key)) { + treeMap.put(selection, key); + } + } + selectionToUniqueStrTreeMap = treeMap; + } + + private Selection getSelectionForOffset(int offset) { + if (isNavigationLinksValid) { + Selection offsetSelection = new Selection(offset, offset); + Selection floorSelection = selectionToUniqueStrTreeMap.floorKey(offsetSelection); + if (floorSelection != null && floorSelection.from <= offset && floorSelection.to > offset) { + return floorSelection; + } + } + return null; + } + + private String getUniqueStrForOffset(int offset) { + Selection selection = getSelectionForOffset(offset); + if (selection != null) { + String uniqueStr = selectionToUniqueStrTreeMap.get(selection); + if (this.isLinkNavigable(uniqueStr) && this.getLinkDescription(uniqueStr) != null) { + return uniqueStr; + } + } + return null; + } + + private Integer getSelectionFromForOffset(int offset) { + Selection selection = getSelectionForOffset(offset); + if (selection != null) { + return selection.from; + } + return null; + } + + private String getLinkDescriptionForOffset(int offset) { + String uniqueStr = getUniqueStrForOffset(offset); + if (uniqueStr != null) { + return this.getLinkDescription(uniqueStr); + } + return null; + } + + private boolean isLinkNavigable(String uniqueStr) { + try { + Boolean isNavigableCached = isNavigableCache.get(uniqueStr); + if (isNavigableCached != null) + return isNavigableCached; + + boolean isNavigable = linkProvider.isLinkNavigable(uniqueStr); + isNavigableCache.put(uniqueStr, isNavigable); + return isNavigable; + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + return false; + } + + private String getLinkDescription(String uniqueStr) { + try { + String descriptionCached = readableLinksCache.get(uniqueStr); + if (descriptionCached != null) + return descriptionCached; + + String description = linkProvider.getLinkDescription(uniqueStr); + if (description != null && description.trim().length() > 0) { + readableLinksCache.put(uniqueStr, description); + return description; + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + return null; + } + + private void onNavigationClicked(String clickedReferenceUniqueStr) { + if (isLocallyNavigable(clickedReferenceUniqueStr)) { + onLocalNavigationRequest(clickedReferenceUniqueStr); + } else if (linkProvider.isLinkNavigable(clickedReferenceUniqueStr)) { + onOutboundNavigationRequest(clickedReferenceUniqueStr); + } else { + JLabel label = this.mainWindow.getLabel(); + if (label == null) + return; + String[] linkParts = clickedReferenceUniqueStr.split("\\|"); + if (linkParts.length <= 1) { + label.setText("Cannot navigate: " + clickedReferenceUniqueStr); + return; + } + String destinationTypeStr = linkParts[1]; + label.setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); + } + } + + private boolean isLocallyNavigable(String uniqueStr) { + return linkProvider.getDefinitionToSelectionMap().containsKey(uniqueStr); + } + + private void onLocalNavigationRequest(String uniqueStr) { + try { + Selection selection = linkProvider.getDefinitionToSelectionMap().get(uniqueStr); + doLocalNavigation(selection); + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void doLocalNavigation(Selection selection) { + try { + textArea.requestFocusInWindow(); + if (selection != null) { + textArea.setSelectionStart(selection.from); + textArea.setSelectionEnd(selection.to); + scrollToSelection(selection.from); + } else { + textArea.setSelectionStart(0); + textArea.setSelectionEnd(0); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + } + + private void scrollToSelection(final int selectionBeginningOffset) { + SwingUtilities.invokeLater(() -> { + try { + int fullHeight = textArea.getBounds().height; + int viewportHeight = textArea.getVisibleRect().height; + int viewportLineCount = viewportHeight / textArea.getLineHeight(); + int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); + int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); + int upperLineToSet = selectionLineNum - upperMarginToScroll; + int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); + + if (selectionLineNum <= currentUpperLine + 2 + || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { + Rectangle rectToScroll = new Rectangle(); + rectToScroll.x = 0; + rectToScroll.width = 1; + rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); + rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); + textArea.scrollRectToVisible(rectToScroll); + } + } catch (Exception e) { + Luyten.showExceptionDialog("Exception!", e); + } + }); + } + + private void onOutboundNavigationRequest(String uniqueStr) { + mainWindow.onNavigationRequest(uniqueStr); + } + + public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, + DecompilationOptions decompilationOptions) { + this.metadataSystem = metadataSystem; + this.settings = settings; + this.decompilationOptions = decompilationOptions; + } + + public TypeDefinition getType() { + return type; + } + + public void setType(TypeDefinition type) { + this.type = type; + } + + public boolean isContentValid() { + return isContentValid; + } + + public void invalidateContent() { + try { + this.setContent(""); + } finally { + this.isContentValid = false; + this.isNavigationLinksValid = false; + } + } + + public void resetScrollPosition() { + lastScrollPercent = null; + } + + public void setInitialNavigationLink(String initialNavigationLink) { + this.initialNavigationLink = initialNavigationLink; + } + + public void onAddedToScreen() { + try { + if (initialNavigationLink != null) { + onLocalNavigationRequest(initialNavigationLink); + } else if (isFirstTimeRun) { + // warm up scrolling + isFirstTimeRun = false; + doLocalNavigation(new Selection(0, 0)); + } + } finally { + initialNavigationLink = null; + } + } + + /** + * sun.swing.CachedPainter holds on OpenFile for a while even after + * JTabbedPane.remove(component) + */ + public void close() { + linkProvider = null; + type = null; + invalidateContent(); + clearLinksCache(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((path == null) ? 0 : path.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OpenFile other = (OpenFile) obj; + if (path == null) { + return other.path == null; + } else return path.equals(other.path); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/RecentFiles.java b/src/main/java/us/deathmarine/luyten/RecentFiles.java new file mode 100644 index 00000000..4633e258 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/RecentFiles.java @@ -0,0 +1,68 @@ +package us.deathmarine.luyten; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +public class RecentFiles { + + public static List paths = new ArrayList<>(); + private static final Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); + + public static int load() { + boolean saveNeeded = false; + + String serializedPaths = prefs.get("recentFiles", null); + + if (serializedPaths == null) return 0; + + // "test","asdf.txt","text2" + for (String path : serializedPaths.split("\",\"")) { + + path = path.replace("\"", ""); + + if (!path.trim().isEmpty() && !new File(path).exists()) saveNeeded = true; + else paths.add(path); + } + + if (saveNeeded) save(); + + return paths.size(); + } + + public static void add(String path) { + if (paths.contains(path)) { + paths.remove(path); + paths.add(path); + return; + } + + if (paths.size() >= 10) paths.remove(0); + paths.add(path); + + save(); + } + + public static void save() { + if (paths.size() == 0) { + prefs.put("recentFiles", ""); + return; + } + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < paths.size(); i++) { + if (i != 0) sb.append(','); + + /*if (!new File(paths.get(i)).exists()) { + paths.remove(i); + continue; + }*/ + sb.append("\"").append(paths.get(i)).append("\""); + } + + prefs.put("recentFiles", sb.toString()); + } + +} diff --git a/src/main/java/us/deathmarine/luyten/Selection.java b/src/main/java/us/deathmarine/luyten/Selection.java new file mode 100644 index 00000000..c53e27fb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/Selection.java @@ -0,0 +1,40 @@ +package us.deathmarine.luyten; + +public class Selection implements Comparable { + + public final Integer from; + public final Integer to; + + public Selection(Integer from, Integer to) { + this.from = from; + this.to = to; + } + + @Override + public int compareTo(Selection o) { + return from.compareTo(o.from); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((from == null) ? 0 : from.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Selection other = (Selection) obj; + if (from == null) { + return other.from == null; + } else return from.equals(other.from); + } + +} diff --git a/src/us/deathmarine/luyten/SystemInfo.java b/src/main/java/us/deathmarine/luyten/SystemInfo.java similarity index 99% rename from src/us/deathmarine/luyten/SystemInfo.java rename to src/main/java/us/deathmarine/luyten/SystemInfo.java index 77722ef3..7f3730cf 100644 --- a/src/us/deathmarine/luyten/SystemInfo.java +++ b/src/main/java/us/deathmarine/luyten/SystemInfo.java @@ -3,8 +3,10 @@ import java.util.Locale; public class SystemInfo { + private static final String OS_NAME = System.getProperty("os.name"); private static final String OS_NAME_LOWER = OS_NAME.toLowerCase(Locale.US); public static boolean IS_MAC = OS_NAME_LOWER.startsWith("mac"); + } diff --git a/src/main/java/us/deathmarine/luyten/TooLargeFileException.java b/src/main/java/us/deathmarine/luyten/TooLargeFileException.java new file mode 100644 index 00000000..79e8b52e --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TooLargeFileException.java @@ -0,0 +1,22 @@ +package us.deathmarine.luyten; + +import java.text.DecimalFormat; + +public class TooLargeFileException extends Exception { + + private static final long serialVersionUID = 6091096838075139962L; + private final long size; + + public TooLargeFileException(long size) { + this.size = size; + } + + public String getReadableFileSize() { + if (size <= 0) + return "0"; + final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; + int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); + return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java b/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java new file mode 100644 index 00000000..136dc3d2 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TreeNodeUserObject.java @@ -0,0 +1,38 @@ +package us.deathmarine.luyten; + +public class TreeNodeUserObject { + + private String originalName; + private String displayName; + + public TreeNodeUserObject(String name) { + this(name, name); + } + + public TreeNodeUserObject(String originalName, String displayName) { + this.originalName = originalName; + this.displayName = displayName; + } + + public String getOriginalName() { + return originalName; + } + + public void setOriginalName(String originalName) { + this.originalName = originalName; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/TreeUtil.java b/src/main/java/us/deathmarine/luyten/TreeUtil.java new file mode 100644 index 00000000..21548784 --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/TreeUtil.java @@ -0,0 +1,82 @@ +package us.deathmarine.luyten; + +import java.util.HashSet; +import java.util.Set; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +public class TreeUtil { + + private JTree tree; + + public TreeUtil() { + } + + public TreeUtil(JTree tree) { + this.tree = tree; + } + + public Set getExpansionState() { + Set openedSet = new HashSet<>(); + if (tree != null) { + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + String rowPathStr = getRowPathStr(path); + // for switching Package Explorer on/off + openedSet.addAll(getAllParentPathsStr(rowPathStr)); + } + } + } + return openedSet; + } + + private Set getAllParentPathsStr(String rowPathStr) { + Set parents = new HashSet<>(); + parents.add(rowPathStr); + if (rowPathStr.contains("/")) { + String[] pathElements = rowPathStr.split("/"); + String path = ""; + for (String pathElement : pathElements) { + path = path + pathElement + "/"; + parents.add(path); + } + } + return parents; + } + + public void restoreExpanstionState(Set expansionState) { + if (tree != null && expansionState != null) { + // tree.getRowCount() changes at tree.expandRow() + for (int i = 0; i < tree.getRowCount(); i++) { + TreePath path = tree.getPathForRow(i); + if (expansionState.contains(getRowPathStr(path))) { + tree.expandRow(i); + } + } + } + } + + private String getRowPathStr(TreePath trp) { + StringBuilder pathStr = new StringBuilder(); + if (trp.getPathCount() > 1) { + for (int i = 1; i < trp.getPathCount(); i++) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); + TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); + pathStr.append(userObject.getOriginalName()).append("/"); + } + } + return pathStr.toString(); + } + + public JTree getTree() { + return tree; + } + + public void setTree(JTree tree) { + this.tree = tree; + } + +} diff --git a/src/main/java/us/deathmarine/luyten/WindowPosition.java b/src/main/java/us/deathmarine/luyten/WindowPosition.java new file mode 100644 index 00000000..547ec1eb --- /dev/null +++ b/src/main/java/us/deathmarine/luyten/WindowPosition.java @@ -0,0 +1,91 @@ +package us.deathmarine.luyten; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Toolkit; +import javax.swing.JDialog; +import javax.swing.JFrame; + +public class WindowPosition { + + private boolean isFullScreen; + private int windowWidth; + private int windowHeight; + private int windowX; + private int windowY; + + public void readPositionFromWindow(JFrame window) { + isFullScreen = (window.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH; + if (!isFullScreen) { + this.readPositionFromComponent(window); + } + } + + public void readPositionFromDialog(JDialog dialog) { + this.readPositionFromComponent(dialog); + } + + private void readPositionFromComponent(Component component) { + isFullScreen = false; + windowWidth = component.getWidth(); + windowHeight = component.getHeight(); + windowX = component.getX(); + windowY = component.getY(); + } + + public boolean isSavedWindowPositionValid() { + if (isFullScreen) { + return true; + } + if (windowWidth < 100 || windowHeight < 100) { + return false; + } + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + if (windowWidth > screenSize.width + 50 || windowHeight > screenSize.height + 50) { + return false; + } + return windowY >= -20 && windowY <= screenSize.height - 50 && windowX >= 50 - windowWidth + && windowX <= screenSize.width - 50; + } + + public boolean isFullScreen() { + return isFullScreen; + } + + public void setFullScreen(boolean isFullScreen) { + this.isFullScreen = isFullScreen; + } + + public int getWindowWidth() { + return windowWidth; + } + + public void setWindowWidth(int windowWidth) { + this.windowWidth = windowWidth; + } + + public int getWindowHeight() { + return windowHeight; + } + + public void setWindowHeight(int windowHeight) { + this.windowHeight = windowHeight; + } + + public int getWindowX() { + return windowX; + } + + public void setWindowX(int windowX) { + this.windowX = windowX; + } + + public int getWindowY() { + return windowY; + } + + public void setWindowY(int windowY) { + this.windowY = windowY; + } + +} diff --git a/src/resources/Luyten.icns b/src/main/resources/Luyten.icns similarity index 100% rename from src/resources/Luyten.icns rename to src/main/resources/Luyten.icns diff --git a/src/resources/Luyten.png b/src/main/resources/Luyten.png similarity index 100% rename from src/resources/Luyten.png rename to src/main/resources/Luyten.png diff --git a/src/distfiles/Procyon.License.txt b/src/main/resources/distfiles/Procyon.License.txt similarity index 99% rename from src/distfiles/Procyon.License.txt rename to src/main/resources/distfiles/Procyon.License.txt index 0325c58b..bb69a347 100644 --- a/src/distfiles/Procyon.License.txt +++ b/src/main/resources/distfiles/Procyon.License.txt @@ -1,32 +1,32 @@ -Procyon -Copyright (c) 2013, Mike Strobel - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -1. Definitions. -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file +Procyon +Copyright (c) 2022, Mike Strobel + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. diff --git a/src/distfiles/RSyntaxTextArea.License.txt b/src/main/resources/distfiles/RSyntaxTextArea.License.txt similarity index 92% rename from src/distfiles/RSyntaxTextArea.License.txt rename to src/main/resources/distfiles/RSyntaxTextArea.License.txt index 398ef073..d1f01ea3 100644 --- a/src/distfiles/RSyntaxTextArea.License.txt +++ b/src/main/resources/distfiles/RSyntaxTextArea.License.txt @@ -1,25 +1,25 @@ -RSyntaxTextArea -Copyright (c) 2012, Robert Futrell -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +RSyntaxTextArea +Copyright (c) 2022, Robert Futrell +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/resources/file.png b/src/main/resources/file.png similarity index 100% rename from src/resources/file.png rename to src/main/resources/file.png diff --git a/src/resources/icon_close.png b/src/main/resources/icon_close.png similarity index 100% rename from src/resources/icon_close.png rename to src/main/resources/icon_close.png diff --git a/src/resources/java.png b/src/main/resources/java.png similarity index 100% rename from src/resources/java.png rename to src/main/resources/java.png diff --git a/src/resources/package_obj.png b/src/main/resources/package_obj.png similarity index 100% rename from src/resources/package_obj.png rename to src/main/resources/package_obj.png diff --git a/src/resources/yml.png b/src/main/resources/yml.png similarity index 100% rename from src/resources/yml.png rename to src/main/resources/yml.png diff --git a/src/us/deathmarine/luyten/CellRenderer.java b/src/us/deathmarine/luyten/CellRenderer.java deleted file mode 100644 index b0fe32df..00000000 --- a/src/us/deathmarine/luyten/CellRenderer.java +++ /dev/null @@ -1,52 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Toolkit; - -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; - -public class CellRenderer extends DefaultTreeCellRenderer { - private static final long serialVersionUID = -5691181006363313993L; - Icon pack; - Icon java_image; - Icon yml_image; - Icon file_image; - - public CellRenderer() { - this.pack = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/package_obj.png"))); - this.java_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/java.png"))); - this.yml_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/yml.png"))); - this.file_image = new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/file.png"))); - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, - int row, boolean hasFocus) { - super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); - DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; - if (node.getChildCount() > 0) { - setIcon(this.pack); - } else if (getFileName(node).endsWith(".class") || getFileName(node).endsWith(".java")) { - setIcon(this.java_image); - } else if (getFileName(node).endsWith(".yml") || getFileName(node).endsWith(".yaml")) { - setIcon(this.yml_image); - } else { - setIcon(this.file_image); - } - - return this; - } - - public String getFileName(DefaultMutableTreeNode node) { - return ((TreeNodeUserObject) node.getUserObject()).getOriginalName(); - } - -} diff --git a/src/us/deathmarine/luyten/Closer.java b/src/us/deathmarine/luyten/Closer.java deleted file mode 100644 index 31dcc15e..00000000 --- a/src/us/deathmarine/luyten/Closer.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.deathmarine.luyten; - -public final class Closer { - public static void tryClose(final AutoCloseable c) { - if (c == null) { - return; - } - try { - c.close(); - } catch (Throwable ignored) { - } - } - - public static void tryClose(final AutoCloseable... items) { - if (items == null) { - return; - } - for (AutoCloseable c : items) { - tryClose(c); - } - } -} diff --git a/src/us/deathmarine/luyten/ConfigSaver.java b/src/us/deathmarine/luyten/ConfigSaver.java deleted file mode 100644 index 6d537589..00000000 --- a/src/us/deathmarine/luyten/ConfigSaver.java +++ /dev/null @@ -1,223 +0,0 @@ -package us.deathmarine.luyten; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.prefs.Preferences; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.languages.Language; -import com.strobel.decompiler.languages.Languages; -import com.strobel.decompiler.languages.java.JavaFormattingOptions; - -public class ConfigSaver { - - private static final String FLATTEN_SWITCH_BLOCKS_ID = "flattenSwitchBlocks"; - private static final String FORCE_EXPLICIT_IMPORTS_ID = "forceExplicitImports"; - private static final String SHOW_SYNTHETIC_MEMBERS_ID = "showSyntheticMembers"; - private static final String EXCLUDE_NESTED_TYPES_ID = "excludeNestedTypes"; - private static final String FORCE_EXPLICIT_TYPE_ARGUMENTS_ID = "forceExplicitTypeArguments"; - private static final String RETAIN_REDUNDANT_CASTS_ID = "retainRedundantCasts"; - private static final String INCLUDE_ERROR_DIAGNOSTICS_ID = "includeErrorDiagnostics"; - private static final String UNICODE_REPLACE_ENABLED_ID = "unicodeReplaceEnabled"; - private static final String LANGUAGE_NAME_ID = "languageName"; - - private static final String MAIN_WINDOW_ID_PREFIX = "main"; - private static final String FIND_WINDOW_ID_PREFIX = "find"; - private static final String WINDOW_IS_FULL_SCREEN_ID = "WindowIsFullScreen"; - private static final String WINDOW_WIDTH_ID = "WindowWidth"; - private static final String WINDOW_HEIGHT_ID = "WindowHeight"; - private static final String WINDOW_X_ID = "WindowX"; - private static final String WINDOW_Y_ID = "WindowY"; - - private DecompilerSettings decompilerSettings; - private WindowPosition mainWindowPosition; - private WindowPosition findWindowPosition; - private LuytenPreferences luytenPreferences; - - private static ConfigSaver theLoadedInstance; - - /** - * Do not instantiate, get the loaded instance - */ - private ConfigSaver() { - } - - public static ConfigSaver getLoadedInstance() { - if (theLoadedInstance == null) { - synchronized (ConfigSaver.class) { - if (theLoadedInstance == null) { - theLoadedInstance = new ConfigSaver(); - theLoadedInstance.loadConfig(); - } - } - } - return theLoadedInstance; - } - - /** - * Do not load, get the loaded instance - */ - private void loadConfig() { - decompilerSettings = new DecompilerSettings(); - if (decompilerSettings.getJavaFormattingOptions() == null) { - decompilerSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); - } - luytenPreferences = new LuytenPreferences(); - mainWindowPosition = new WindowPosition(); - findWindowPosition = new WindowPosition(); - try { - Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); - if (!prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()) - .equals(decompilerSettings.getLanguage().getName())) - prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); - - decompilerSettings.setFlattenSwitchBlocks( - prefs.getBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks())); - decompilerSettings.setForceExplicitImports( - prefs.getBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports())); - decompilerSettings.setShowSyntheticMembers( - prefs.getBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers())); - decompilerSettings.setExcludeNestedTypes( - prefs.getBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes())); - decompilerSettings.setForceExplicitTypeArguments(prefs.getBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, - decompilerSettings.getForceExplicitTypeArguments())); - decompilerSettings.setRetainRedundantCasts( - prefs.getBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts())); - decompilerSettings.setIncludeErrorDiagnostics( - prefs.getBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics())); - decompilerSettings.setLanguage( - findLanguageByName(prefs.get(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()))); - decompilerSettings.setUnicodeOutputEnabled(prefs.getBoolean(UNICODE_REPLACE_ENABLED_ID, false)); - - mainWindowPosition = loadWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX); - findWindowPosition = loadWindowPosition(prefs, FIND_WINDOW_ID_PREFIX); - luytenPreferences = loadLuytenPreferences(prefs); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private WindowPosition loadWindowPosition(Preferences prefs, String windowIdPrefix) { - WindowPosition windowPosition = new WindowPosition(); - windowPosition.setFullScreen(prefs.getBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, false)); - windowPosition.setWindowWidth(prefs.getInt(windowIdPrefix + WINDOW_WIDTH_ID, 0)); - windowPosition.setWindowHeight(prefs.getInt(windowIdPrefix + WINDOW_HEIGHT_ID, 0)); - windowPosition.setWindowX(prefs.getInt(windowIdPrefix + WINDOW_X_ID, 0)); - windowPosition.setWindowY(prefs.getInt(windowIdPrefix + WINDOW_Y_ID, 0)); - return windowPosition; - } - - // load preferences by their java variable names - private LuytenPreferences loadLuytenPreferences(Preferences prefs) throws Exception { - LuytenPreferences newLuytenPrefs = new LuytenPreferences(); - for (Field field : LuytenPreferences.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) - continue; - field.setAccessible(true); - String prefId = field.getName(); - Object defaultVal = field.get(newLuytenPrefs); - - if (field.getType() == String.class) { - String defaultStr = (String) (defaultVal == null ? "" : defaultVal); - field.set(newLuytenPrefs, prefs.get(prefId, defaultStr)); - - } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - Boolean defaultBool = (Boolean) (defaultVal == null ? new Boolean(false) : defaultVal); - field.setBoolean(newLuytenPrefs, prefs.getBoolean(prefId, defaultBool)); - - } else if (field.getType() == Integer.class || field.getType() == int.class) { - Integer defaultInt = (Integer) (defaultVal == null ? new Integer(0) : defaultVal); - field.setInt(newLuytenPrefs, prefs.getInt(prefId, defaultInt)); - } - } - return newLuytenPrefs; - } - - public void saveConfig() { - // Registry path on Windows Xp: - // HKEY_CURRENT_USER/Software/JavaSoft/Prefs/us/deathmarine/luyten - try { - Preferences prefs = Preferences.userNodeForPackage(ConfigSaver.class); - - prefs.putBoolean(FLATTEN_SWITCH_BLOCKS_ID, decompilerSettings.getFlattenSwitchBlocks()); - prefs.putBoolean(FORCE_EXPLICIT_IMPORTS_ID, decompilerSettings.getForceExplicitImports()); - prefs.putBoolean(SHOW_SYNTHETIC_MEMBERS_ID, decompilerSettings.getShowSyntheticMembers()); - prefs.putBoolean(EXCLUDE_NESTED_TYPES_ID, decompilerSettings.getExcludeNestedTypes()); - prefs.putBoolean(FORCE_EXPLICIT_TYPE_ARGUMENTS_ID, decompilerSettings.getForceExplicitTypeArguments()); - prefs.putBoolean(RETAIN_REDUNDANT_CASTS_ID, decompilerSettings.getRetainRedundantCasts()); - prefs.putBoolean(INCLUDE_ERROR_DIAGNOSTICS_ID, decompilerSettings.getIncludeErrorDiagnostics()); - prefs.putBoolean(UNICODE_REPLACE_ENABLED_ID, decompilerSettings.isUnicodeOutputEnabled()); - prefs.put(LANGUAGE_NAME_ID, decompilerSettings.getLanguage().getName()); - - saveWindowPosition(prefs, MAIN_WINDOW_ID_PREFIX, mainWindowPosition); - saveWindowPosition(prefs, FIND_WINDOW_ID_PREFIX, findWindowPosition); - saveLuytenPreferences(prefs); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void saveWindowPosition(Preferences prefs, String windowIdPrefix, WindowPosition windowPosition) { - prefs.putBoolean(windowIdPrefix + WINDOW_IS_FULL_SCREEN_ID, windowPosition.isFullScreen()); - prefs.putInt(windowIdPrefix + WINDOW_WIDTH_ID, windowPosition.getWindowWidth()); - prefs.putInt(windowIdPrefix + WINDOW_HEIGHT_ID, windowPosition.getWindowHeight()); - prefs.putInt(windowIdPrefix + WINDOW_X_ID, windowPosition.getWindowX()); - prefs.putInt(windowIdPrefix + WINDOW_Y_ID, windowPosition.getWindowY()); - } - - // save preferences by their java variable names - private void saveLuytenPreferences(Preferences prefs) throws Exception { - for (Field field : LuytenPreferences.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) - continue; - field.setAccessible(true); - String prefId = field.getName(); - Object value = field.get(luytenPreferences); - - if (field.getType() == String.class) { - prefs.put(prefId, (String) (value == null ? "" : value)); - - } else if (field.getType() == Boolean.class || field.getType() == boolean.class) { - prefs.putBoolean(prefId, (Boolean) (value == null ? new Boolean(false) : value)); - - } else if (field.getType() == Integer.class || field.getType() == int.class) { - prefs.putInt(prefId, (Integer) (value == null ? new Integer(0) : value)); - } - } - } - - private Language findLanguageByName(String languageName) { - if (languageName != null) { - - if (languageName.equals(Languages.java().getName())) { - return Languages.java(); - } else if (languageName.equals(Languages.bytecode().getName())) { - return Languages.bytecode(); - } else if (languageName.equals(Languages.bytecodeAst().getName())) { - return Languages.bytecodeAst(); - } - - for (Language language : Languages.debug()) { - if (languageName.equals(language.getName())) { - return language; - } - } - } - return Languages.java(); - } - - public DecompilerSettings getDecompilerSettings() { - return decompilerSettings; - } - - public WindowPosition getMainWindowPosition() { - return mainWindowPosition; - } - - public WindowPosition getFindWindowPosition() { - return findWindowPosition; - } - - public LuytenPreferences getLuytenPreferences() { - return luytenPreferences; - } -} diff --git a/src/us/deathmarine/luyten/DecompilerLinkProvider.java b/src/us/deathmarine/luyten/DecompilerLinkProvider.java deleted file mode 100644 index b63d9ad6..00000000 --- a/src/us/deathmarine/luyten/DecompilerLinkProvider.java +++ /dev/null @@ -1,384 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.StringWriter; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import com.strobel.assembler.metadata.FieldDefinition; -import com.strobel.assembler.metadata.FieldReference; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.MethodDefinition; -import com.strobel.assembler.metadata.MethodReference; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -public class DecompilerLinkProvider implements LinkProvider { - - private Map definitionToSelectionMap = new HashMap<>(); - private Map> referenceToSelectionsMap = new HashMap<>(); - private boolean isSelectionMapsPopulated = false; - - private MetadataSystem metadataSystem; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; - private TypeDefinition type; - - private String currentTypeQualifiedName; - private String textContent = ""; - - @Override - public void generateContent() { - definitionToSelectionMap = new HashMap<>(); - referenceToSelectionsMap = new HashMap<>(); - currentTypeQualifiedName = type.getPackageName() + "." + type.getName(); - final StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter) { - @Override - public void writeDefinition(String text, Object definition, boolean isLocal) { - super.writeDefinition(text, definition, isLocal); - try { - if (text != null && definition != null) { - String uniqueStr = createUniqueStrForReference(definition); - if (uniqueStr != null) { - // fix link's underline length: _java.util.HashSet_ - // -> _HashSet_ - text = text.replaceAll("[^\\.]*\\.", ""); - int from = stringwriter.getBuffer().length() - text.length(); - int to = stringwriter.getBuffer().length(); - definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); - } - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - @Override - public void writeReference(String text, Object reference, boolean isLocal) { - super.writeReference(text, reference, isLocal); - try { - if (text != null && reference != null) { - String uniqueStr = createUniqueStrForReference(reference); - if (uniqueStr != null) { - text = text.replaceAll("[^\\.]*\\.", ""); - int from = stringwriter.getBuffer().length() - text.length(); - int to = stringwriter.getBuffer().length(); - if (reference instanceof FieldReference) { - // fix enum definition links (note: could not fix enum reference links) - if (((FieldReference) reference).isDefinition()) { - definitionToSelectionMap.put(uniqueStr, new Selection(from, to)); - return; - } - } - if (referenceToSelectionsMap.containsKey(uniqueStr)) { - Set selectionsSet = referenceToSelectionsMap.get(uniqueStr); - if (selectionsSet != null) { - selectionsSet.add(new Selection(from, to)); - } - } else { - Set selectionsSet = new HashSet<>(); - selectionsSet.add(new Selection(from, to)); - referenceToSelectionsMap.put(uniqueStr, selectionsSet); - } - } - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }; - plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); - textContent = stringwriter.toString(); - isSelectionMapsPopulated = true; - } - - private String createUniqueStrForReference(Object reference) { - String uniqueStr = null; - if (reference instanceof TypeReference) { - TypeReference type = (TypeReference) reference; - String pathAndTypeStr = getPathAndTypeStr(type); - if (pathAndTypeStr != null) { - uniqueStr = "type|" + pathAndTypeStr; - } - } else if (reference instanceof MethodReference) { - MethodReference method = (MethodReference) reference; - String pathAndTypeStr = getPathAndTypeStr(method.getDeclaringType()); - if (pathAndTypeStr != null) { - uniqueStr = "method|" + pathAndTypeStr + "|" + method.getName() + "|" + method.getErasedSignature(); - } - } else if (reference instanceof FieldReference) { - FieldReference field = (FieldReference) reference; - String pathAndTypeStr = getPathAndTypeStr(field.getDeclaringType()); - if (pathAndTypeStr != null) { - uniqueStr = "field|" + pathAndTypeStr + "|" + field.getName(); - } - } - return uniqueStr; - } - - private String getPathAndTypeStr(TypeReference typeRef) { - String name = typeRef.getName(); - String packageStr = typeRef.getPackageName(); - TypeReference mostOuterTypeRef = getMostOuterTypeRef(typeRef); - String mostOuterTypeName = mostOuterTypeRef.getName(); - if (name != null && packageStr != null && mostOuterTypeName != null && name.trim().length() > 0 - && mostOuterTypeName.trim().length() > 0) { - String pathStr = packageStr.replaceAll("\\.", "/") + "/" + mostOuterTypeName; - String typeStr = packageStr + "." + name.replace(".", "$"); - return pathStr + "|" + typeStr; - } - return null; - } - - private TypeReference getMostOuterTypeRef(TypeReference typeRef) { - int maxDecraringDepth = typeRef.getFullName().split("(\\.|\\$)").length; - for (int i = 0; i < maxDecraringDepth; i++) { - TypeReference declaringTypeRef = typeRef.getDeclaringType(); - if (declaringTypeRef == null) { - break; - } else { - typeRef = declaringTypeRef; - } - } - if (typeRef.getName().contains("$")) { - return getMostOuterTypeRefBySlowLookuping(typeRef); - } - return typeRef; - } - - private TypeReference getMostOuterTypeRefBySlowLookuping(TypeReference typeRef) { - String name = typeRef.getName(); - if (name == null) - return typeRef; - String packageName = typeRef.getPackageName(); - if (packageName == null) - return typeRef; - String[] nameParts = name.split("\\$"); - String newName = ""; - String sep = ""; - for (int i = 0; i < nameParts.length - 1; i++) { - newName = newName + sep + nameParts[i]; - sep = "$"; - String newInternalName = packageName.replaceAll("\\.", "/") + "/" + newName; - TypeReference newTypeRef = metadataSystem.lookupType(newInternalName); - if (newTypeRef != null) { - TypeDefinition newTypeDef = newTypeRef.resolve(); - if (newTypeDef != null) { - return newTypeRef; - } - } - } - return typeRef; - } - - @Override - public String getTextContent() { - return textContent; - } - - @Override - public void processLinks() { - } - - @Override - public Map getDefinitionToSelectionMap() { - return definitionToSelectionMap; - } - - @Override - public Map> getReferenceToSelectionsMap() { - return referenceToSelectionsMap; - } - - @Override - public boolean isLinkNavigable(String uniqueStr) { - if (isSelectionMapsPopulated && definitionToSelectionMap.containsKey(uniqueStr)) - return true; - if (uniqueStr == null) - return false; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length < 3) - return false; - String typeStr = linkParts[2]; - if (typeStr.trim().length() <= 0) - return false; - TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); - if (typeRef == null) - return false; - TypeDefinition typeDef = typeRef.resolve(); - if (typeDef == null) - return false; - if (typeDef.isSynthetic()) - return false; - - if (isSelectionMapsPopulated) { - // current type's navigable definitions checked already, now it's erroneous - if (currentTypeQualifiedName == null || currentTypeQualifiedName.trim().length() <= 0) - return false; - if (typeStr.equals(currentTypeQualifiedName) || typeStr.startsWith(currentTypeQualifiedName + ".") - || typeStr.startsWith(currentTypeQualifiedName + "$")) - return false; - } - - // check linked field/method exists - if (uniqueStr.startsWith("method")) { - if (findMethodInType(typeDef, uniqueStr) == null) { - return false; - } - } else if (uniqueStr.startsWith("field")) { - if (findFieldInType(typeDef, uniqueStr) == null) { - return false; - } - } - return true; - } - - private MethodDefinition findMethodInType(TypeDefinition typeDef, String uniqueStr) { - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length != 5) - return null; - String methodName = linkParts[3]; - String methodErasedSignature = linkParts[4]; - if (methodName.trim().length() <= 0 || methodErasedSignature.trim().length() <= 0) - return null; - List declaredMethods = typeDef.getDeclaredMethods(); - if (declaredMethods == null) - return null; - boolean isFound = false; - for (MethodDefinition declaredMethod : declaredMethods) { - isFound = (declaredMethod != null && methodName.equals(declaredMethod.getName())); - isFound = (isFound && methodErasedSignature.equals(declaredMethod.getErasedSignature())); - if (isFound) { - if (declaredMethod.isSynthetic() && !settings.getShowSyntheticMembers()) { - return null; - } else { - return declaredMethod; - } - } - } - return null; - } - - private FieldDefinition findFieldInType(TypeDefinition typeDef, String uniqueStr) { - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length != 4) - return null; - String fieldName = linkParts[3]; - if (fieldName.trim().length() <= 0) - return null; - List declaredFields = typeDef.getDeclaredFields(); - if (declaredFields == null) - return null; - boolean isFound = false; - for (FieldDefinition declaredField : declaredFields) { - isFound = (declaredField != null && fieldName.equals(declaredField.getName())); - if (isFound) { - if (declaredField.isSynthetic()) { - return null; - } else { - return declaredField; - } - } - } - return null; - } - - @Override - public String getLinkDescription(String uniqueStr) { - String readableLink = null; - try { - if (uniqueStr == null) - return null; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length < 3) - return null; - String typeStr = linkParts[2]; - TypeReference typeRef = metadataSystem.lookupType(typeStr.replaceAll("\\.", "/")); - if (typeRef == null) - return null; - TypeDefinition typeDef = typeRef.resolve(); - if (typeDef == null) - return null; - - String declaredSuffix = ""; - String mostOuterTypeStr = linkParts[1].replaceAll("/", "."); - boolean isOwnFile = mostOuterTypeStr.equals(currentTypeQualifiedName); - if (!isOwnFile) { - declaredSuffix = " - Declared: " + mostOuterTypeStr; - } - - if (uniqueStr.startsWith("type")) { - String desc = typeDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - readableLink = desc; - } - } else if (uniqueStr.startsWith("method")) { - MethodDefinition methodDef = findMethodInType(typeDef, uniqueStr); - if (methodDef == null) - return null; - String desc = methodDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - - if (desc.contains("void ")) { - String constructorName = typeDef.getName(); - TypeReference declaringTypeRef = typeRef.getDeclaringType(); - if (declaringTypeRef != null) { - TypeDefinition declaringTypeDef = declaringTypeRef.resolve(); - if (declaringTypeDef != null) { - String declaringTypeName = declaringTypeDef.getName(); - if (declaringTypeName != null) { - constructorName = StringUtilities.removeLeft(constructorName, declaringTypeName); - constructorName = constructorName.replaceAll("^(\\.|\\$)", ""); - } - } - } - desc = desc.replace("void ", constructorName); - readableLink = "Constructor: " + erasePackageInfoFromDesc(desc) + declaredSuffix; - } else { - readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; - } - } - } else if (uniqueStr.startsWith("field")) { - FieldDefinition fieldDef = findFieldInType(typeDef, uniqueStr); - if (fieldDef == null) - return null; - String desc = fieldDef.getBriefDescription(); - if (desc != null && desc.trim().length() > 0) { - readableLink = erasePackageInfoFromDesc(desc) + declaredSuffix; - } - - } - if (readableLink != null) { - readableLink = readableLink.replace("$", "."); - } - } catch (Exception e) { - readableLink = null; - Luyten.showExceptionDialog("Exception!", e); - } - return readableLink; - } - - private String erasePackageInfoFromDesc(String desc) { - String limiters = "\\(\\)\\<\\>\\[\\]\\?\\s,"; - desc = desc.replaceAll("(?<=[^" + limiters + "]*)([^" + limiters + "]*)\\.", ""); - return desc; - } - - public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, - DecompilationOptions decompilationOptions) { - this.metadataSystem = metadataSystem; - this.settings = settings; - this.decompilationOptions = decompilationOptions; - } - - public void setType(TypeDefinition type) { - this.type = type; - } -} diff --git a/src/us/deathmarine/luyten/DropListener.java b/src/us/deathmarine/luyten/DropListener.java deleted file mode 100644 index ff32a01d..00000000 --- a/src/us/deathmarine/luyten/DropListener.java +++ /dev/null @@ -1,108 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DropTargetDragEvent; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.dnd.DropTargetEvent; -import java.awt.dnd.DropTargetListener; -import java.io.BufferedReader; -import java.io.File; -import java.io.Reader; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -/** - * Drag-Drop (only MainWindow should be called from here) - */ -public class DropListener implements DropTargetListener { - private MainWindow mainWindow; - - public DropListener(MainWindow mainWindow) { - this.mainWindow = mainWindow; - } - - @SuppressWarnings("unchecked") - @Override - public void drop(DropTargetDropEvent event) { - event.acceptDrop(DnDConstants.ACTION_COPY); - Transferable transferable = event.getTransferable(); - if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - for (DataFlavor flavor : flavors) { - try { - if (flavor.isFlavorJavaFileListType()) { - List files = (List) transferable.getTransferData(flavor); - if (files.size() > 1) { - event.rejectDrop(); - return; - } - if (files.size() == 1) { - mainWindow.onFileDropped(files.get(0)); - } - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - event.dropComplete(true); - } else { - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - boolean handled = false; - for (int zz = 0; zz < flavors.length; zz++) { - if (flavors[zz].isRepresentationClassReader()) { - try { - Reader reader = flavors[zz].getReaderForText(transferable); - BufferedReader br = new BufferedReader(reader); - List list = new ArrayList(); - String line = null; - while ((line = br.readLine()) != null) { - try { - if (new String("" + (char) 0).equals(line)) - continue; - File file = new File(new URI(line)); - list.add(file); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - if (list.size() > 1) { - event.rejectDrop(); - return; - } - if (list.size() == 1) { - mainWindow.onFileDropped(list.get(0)); - } - event.getDropTargetContext().dropComplete(true); - handled = true; - } catch (Exception e) { - e.printStackTrace(); - } - break; - } - } - if (!handled) { - event.rejectDrop(); - } - } - - } - - @Override - public void dragEnter(DropTargetDragEvent arg0) { - } - - @Override - public void dragExit(DropTargetEvent arg0) { - } - - @Override - public void dragOver(DropTargetDragEvent arg0) { - } - - @Override - public void dropActionChanged(DropTargetDragEvent arg0) { - } -} diff --git a/src/us/deathmarine/luyten/FileDialog.java b/src/us/deathmarine/luyten/FileDialog.java deleted file mode 100644 index b573ac87..00000000 --- a/src/us/deathmarine/luyten/FileDialog.java +++ /dev/null @@ -1,135 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.io.File; -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileFilter; - -/** - * FileChoosers for Open and Save - */ -public class FileDialog { - private final DirPreferences dirPreferences; - private ConfigSaver configSaver; - private Component parent; - private JFileChooser fcOpen; - private JFileChooser fcSave; - private JFileChooser fcSaveAll; - - public FileDialog(Component parent) { - this.parent = parent; - configSaver = ConfigSaver.getLoadedInstance(); - LuytenPreferences luytenPrefs = configSaver.getLuytenPreferences(); - dirPreferences = new DirPreferences(luytenPrefs); - - new Thread() { - public void run() { - try { - initOpenDialog(); - Thread.sleep(500); - initSaveAllDialog(); - Thread.sleep(500); - initSaveDialog(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - }; - }.start(); - } - - public File doOpenDialog() { - File selectedFile = null; - initOpenDialog(); - - dirPreferences.retrieveOpenDialogDir(fcOpen); - int returnVal = fcOpen.showOpenDialog(parent); - dirPreferences.saveOpenDialogDir(fcOpen); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcOpen.getSelectedFile(); - } - return selectedFile; - } - - public File doSaveDialog(String recommendedFileName) { - File selectedFile = null; - initSaveDialog(); - - dirPreferences.retrieveSaveDialogDir(fcSave); - fcSave.setSelectedFile(new File(recommendedFileName)); - int returnVal = fcSave.showSaveDialog(parent); - dirPreferences.saveSaveDialogDir(fcSave); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcSave.getSelectedFile(); - } - return selectedFile; - } - - public File doSaveAllDialog(String recommendedFileName) { - File selectedFile = null; - initSaveAllDialog(); - - dirPreferences.retrieveSaveDialogDir(fcSaveAll); - fcSaveAll.setSelectedFile(new File(recommendedFileName)); - int returnVal = fcSaveAll.showSaveDialog(parent); - dirPreferences.saveSaveDialogDir(fcSaveAll); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - selectedFile = fcSaveAll.getSelectedFile(); - } - return selectedFile; - } - - public synchronized void initOpenDialog() { - if (fcOpen == null) { - fcOpen = createFileChooser("*.jar", "*.zip", "*.class"); - dirPreferences.retrieveOpenDialogDir(fcOpen); - } - } - - public synchronized void initSaveDialog() { - if (fcSave == null) { - fcSave = createFileChooser("*.txt", "*.java"); - dirPreferences.retrieveSaveDialogDir(fcSave); - } - } - - public synchronized void initSaveAllDialog() { - if (fcSaveAll == null) { - fcSaveAll = createFileChooser("*.jar", "*.zip"); - dirPreferences.retrieveSaveDialogDir(fcSaveAll); - } - } - - private JFileChooser createFileChooser(String... fileFilters) { - JFileChooser fc = new JFileChooser(); - for (String fileFilter : fileFilters) { - fc.addChoosableFileFilter(new FileChooserFileFilter(fileFilter)); - } - fc.setFileSelectionMode(JFileChooser.FILES_ONLY); - fc.setMultiSelectionEnabled(false); - return fc; - } - - public class FileChooserFileFilter extends FileFilter { - String objType; - - public FileChooserFileFilter(String string) { - objType = string; - } - - @Override - public boolean accept(File f) { - if (f.isDirectory()) - return true; - return f.getName().toLowerCase().endsWith(objType.substring(1)); - } - - @Override - public String getDescription() { - return objType; - } - } - -} diff --git a/src/us/deathmarine/luyten/FileSaver.java b/src/us/deathmarine/luyten/FileSaver.java deleted file mode 100644 index e6ee49b0..00000000 --- a/src/us/deathmarine/luyten/FileSaver.java +++ /dev/null @@ -1,334 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedOutputStream; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.zip.ZipException; -import java.util.zip.ZipOutputStream; - -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; - -import com.strobel.assembler.metadata.ITypeLoader; -import com.strobel.assembler.metadata.JarTypeLoader; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; -import com.strobel.decompiler.languages.java.JavaFormattingOptions; - -/** - * Performs Save and Save All - */ -public class FileSaver { - - private JProgressBar bar; - private JLabel label; - private boolean cancel; - private boolean extracting; - - public FileSaver(JProgressBar bar, JLabel label) { - this.bar = bar; - this.label = label; - final JPopupMenu menu = new JPopupMenu("Cancel"); - final JMenuItem item = new JMenuItem("Cancel"); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - setCancel(true); - } - }); - menu.add(item); - this.label.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent ev) { - if (SwingUtilities.isRightMouseButton(ev) && isExtracting()) - menu.show(ev.getComponent(), ev.getX(), ev.getY()); - } - }); - } - - public void saveText(final String text, final File file) { - new Thread(new Runnable() { - @Override - public void run() { - DecompilerSettings settings = cloneSettings(); - boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); - long time = System.currentTimeMillis(); - try (FileOutputStream fos = new FileOutputStream(file); - OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") - : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer);) { - label.setText("Extracting: " + file.getName()); - bar.setVisible(true); - bw.write(text); - bw.flush(); - label.setText("Completed: " + getTime(time)); - } catch (Exception e1) { - label.setText("Cannot save file: " + file.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - } - }).start(); - } - - public void saveAllDecompiled(final File inFile, final File outFile) { - new Thread(new Runnable() { - @Override - public void run() { - long time = System.currentTimeMillis(); - try { - bar.setVisible(true); - setExtracting(true); - label.setText("Extracting: " + outFile.getName()); - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - String inFileName = inFile.getName().toLowerCase(); - - if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { - doSaveJarDecompiled(inFile, outFile); - } else if (inFileName.endsWith(".class")) { - doSaveClassDecompiled(inFile, outFile); - } else { - doSaveUnknownFile(inFile, outFile); - } - if (cancel) { - label.setText("Cancelled"); - outFile.delete(); - setCancel(false); - } else { - label.setText("Completed: " + getTime(time)); - } - } catch (Exception e1) { - label.setText("Cannot save file: " + outFile.getName()); - Luyten.showExceptionDialog("Unable to save file!\n", e1); - } finally { - setExtracting(false); - bar.setVisible(false); - } - } - }).start(); - } - - private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { - try (JarFile jfile = new JarFile(inFile); - FileOutputStream dest = new FileOutputStream(outFile); - BufferedOutputStream buffDest = new BufferedOutputStream(dest); - ZipOutputStream out = new ZipOutputStream(buffDest);) { - bar.setMinimum(0); - bar.setMaximum(jfile.size()); - byte data[] = new byte[1024]; - DecompilerSettings settings = cloneSettings(); - LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - ITypeLoader jarLoader = new JarTypeLoader(jfile); - typeLoader.getTypeLoaders().add(jarLoader); - - DecompilationOptions decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - - List mass = null; - JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); - if (luytenPrefs.isFilterOutInnerClassEntries()) { - mass = jarEntryFilter.getEntriesWithoutInnerClasses(); - } else { - mass = jarEntryFilter.getAllEntriesFromJar(); - } - - Enumeration ent = jfile.entries(); - Set history = new HashSet(); - int tick = 0; - while (ent.hasMoreElements() && !cancel) { - bar.setValue(++tick); - JarEntry entry = ent.nextElement(); - if (!mass.contains(entry.getName())) - continue; - label.setText("Extracting: " + entry.getName()); - bar.setVisible(true); - if (entry.getName().endsWith(".class")) { - JarEntry etn = new JarEntry(entry.getName().replace(".class", ".java")); - label.setText("Extracting: " + etn.getName()); - System.out.println("[SaveAll]: " + etn.getName() + " -> " + outFile.getName()); - - if (history.add(etn.getName())) { - out.putNextEntry(etn); - try { - boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); - String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; - if ((type == null) || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, "UTF-8") - : new OutputStreamWriter(out); - PlainTextOutput plainTextOutput = new PlainTextOutput(writer); - plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - writer.flush(); - } catch (Exception e) { - label.setText("Cannot decompile file: " + entry.getName()); - Luyten.showExceptionDialog("Unable to Decompile file!\nSkipping file...", e); - } finally { - out.closeEntry(); - } - } - } else { - try { - JarEntry etn = new JarEntry(entry.getName()); - if (entry.getName().endsWith(".java")) - etn = new JarEntry(entry.getName().replace(".java", ".src.java")); - if (history.add(etn.getName())) { - out.putNextEntry(etn); - try { - InputStream in = jfile.getInputStream(etn); - if (in != null) { - try { - int count; - while ((count = in.read(data, 0, 1024)) != -1) { - out.write(data, 0, count); - } - } finally { - in.close(); - } - } - } finally { - out.closeEntry(); - } - } - } catch (ZipException ze) { - if (!ze.getMessage().contains("duplicate")) { - throw ze; - } - } - } - } - } - } - - private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { - DecompilerSettings settings = cloneSettings(); - LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - TypeReference type = metadataSystem.lookupType(inFile.getCanonicalPath()); - - DecompilationOptions decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - - boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); - TypeDefinition resolvedType = null; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - String decompiledSource = stringwriter.toString(); - - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - try (FileOutputStream fos = new FileOutputStream(outFile); - OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") - : new OutputStreamWriter(fos); - BufferedWriter bw = new BufferedWriter(writer);) { - bw.write(decompiledSource); - bw.flush(); - } - } - - private void doSaveUnknownFile(File inFile, File outFile) throws Exception { - try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile);) { - System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); - - byte data[] = new byte[1024]; - int count; - while ((count = in.read(data, 0, 1024)) != -1) { - out.write(data, 0, count); - } - } - } - - private DecompilerSettings cloneSettings() { - DecompilerSettings settings = ConfigSaver.getLoadedInstance().getDecompilerSettings(); - DecompilerSettings newSettings = new DecompilerSettings(); - if (newSettings.getJavaFormattingOptions() == null) { - newSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); - } - // synchronized: against main menu changes - synchronized (settings) { - newSettings.setExcludeNestedTypes(settings.getExcludeNestedTypes()); - newSettings.setFlattenSwitchBlocks(settings.getFlattenSwitchBlocks()); - newSettings.setForceExplicitImports(settings.getForceExplicitImports()); - newSettings.setForceExplicitTypeArguments(settings.getForceExplicitTypeArguments()); - newSettings.setOutputFileHeaderText(settings.getOutputFileHeaderText()); - newSettings.setLanguage(settings.getLanguage()); - newSettings.setShowSyntheticMembers(settings.getShowSyntheticMembers()); - newSettings.setAlwaysGenerateExceptionVariableForCatchBlocks( - settings.getAlwaysGenerateExceptionVariableForCatchBlocks()); - newSettings.setOutputDirectory(settings.getOutputDirectory()); - newSettings.setRetainRedundantCasts(settings.getRetainRedundantCasts()); - newSettings.setIncludeErrorDiagnostics(settings.getIncludeErrorDiagnostics()); - newSettings.setIncludeLineNumbersInBytecode(settings.getIncludeLineNumbersInBytecode()); - newSettings.setRetainPointlessSwitches(settings.getRetainPointlessSwitches()); - newSettings.setUnicodeOutputEnabled(settings.isUnicodeOutputEnabled()); - newSettings.setMergeVariables(settings.getMergeVariables()); - newSettings.setShowDebugLineNumbers(settings.getShowDebugLineNumbers()); - } - return newSettings; - } - - public boolean isCancel() { - return cancel; - } - - public void setCancel(boolean cancel) { - this.cancel = cancel; - } - - public boolean isExtracting() { - return extracting; - } - - public void setExtracting(boolean extracting) { - this.extracting = extracting; - } - - public static String getTime(long time) { - long lap = System.currentTimeMillis() - time; - lap = lap / 1000; - StringBuilder sb = new StringBuilder(); - long hour = ((lap / 60) / 60); - long min = ((lap - (hour * 60 * 60)) / 60); - long sec = ((lap - (hour * 60 * 60) - (min * 60))); - if (hour > 0) - sb.append("Hour:").append(hour).append(" "); - sb.append("Min(s): ").append(min).append(" Sec: ").append(sec); - return sb.toString(); - } -} diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java deleted file mode 100644 index bf358491..00000000 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ /dev/null @@ -1,372 +0,0 @@ -package us.deathmarine.luyten; - -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -import us.deathmarine.luyten.ConfigSaver; -import us.deathmarine.luyten.MainWindow; -import us.deathmarine.luyten.Model; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.util.Collections; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.regex.Pattern; - -import javax.swing.*; -import javax.swing.GroupLayout.Alignment; - -/** - * - * this is the Find All Dialog - *

- * Change with 1.1 - * Adjust the find all box width - *

- * - * @author clevertension - * @version 1.1 - */ -public class FindAllBox extends JDialog { - private static final long serialVersionUID = -4125409760166690462L; - private static final int MIN_WIDTH = 640; - private boolean searching; - - private JButton findButton; - private JTextField textField; - private JCheckBox mcase; - private JCheckBox regex; - private JCheckBox wholew; - private JCheckBox classname; - private JList list; - private JProgressBar progressBar; - boolean locked; - - private JLabel statusLabel = new JLabel(""); - - private DefaultListModel classesList = new DefaultListModel(); - - private Thread tmp_thread; - - private MainWindow mainWindow; - - public FindAllBox(final MainWindow mainWindow) { - this.setDefaultCloseOperation(HIDE_ON_CLOSE); - this.setHideOnEscapeButton(); - - progressBar = new JProgressBar(0, 100); - this.mainWindow = mainWindow; - - JLabel label = new JLabel("Find What:"); - textField = new JTextField(); - findButton = new JButton("Find"); - findButton.addActionListener(new FindButton()); - - mcase = new JCheckBox("Match Case"); - regex = new JCheckBox("Regex"); - wholew = new JCheckBox("Whole Words"); - classname = new JCheckBox("Classnames"); - - this.getRootPane().setDefaultButton(findButton); - - list = new JList(classesList); - list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); - list.setLayoutOrientation(JList.VERTICAL_WRAP); - list.setVisibleRowCount(-1); - list.addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent evt) { - @SuppressWarnings("unchecked") - JList list = (JList) evt.getSource(); - if (evt.getClickCount() == 2) { - int index = list.locationToIndex(evt.getPoint()); - String entryName = (String) list.getModel().getElementAt(index); - String[] array = entryName.split("/"); - if (entryName.toLowerCase().endsWith(".class")) { - String internalName = StringUtilities.removeRight(entryName, ".class"); - TypeReference type = Model.metadataSystem.lookupType(internalName); - try { - mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], entryName, - null); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - } else { - try { - JarFile jfile = new JarFile(mainWindow.getSelectedModel().getOpenedFile()); - mainWindow.getSelectedModel().extractSimpleFileEntryToTextPane( - jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], - entryName); - jfile.close(); - } catch (IOException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - } - }); - list.setLayoutOrientation(JList.VERTICAL); - JScrollPane listScroller = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - int width = (int) (screenSize.width * 0.35); - if (width < MIN_WIDTH) { - width = MIN_WIDTH; - } - final Dimension center = new Dimension(width, 500); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - this.setResizable(false); - - GroupLayout layout = new GroupLayout(getRootPane()); - getRootPane().setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setHorizontalGroup( - layout.createSequentialGroup().addComponent(label) - .addGroup( - layout.createParallelGroup(Alignment.LEADING).addComponent(statusLabel) - .addComponent(textField) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addComponent(mcase)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(wholew)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(classname))) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(listScroller) - .addComponent(progressBar)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton)) - - ); - - layout.linkSize(SwingConstants.HORIZONTAL, findButton); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) - .addComponent(findButton)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase).addComponent(wholew) - .addComponent(regex).addComponent(classname)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(listScroller)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING)).addComponent(statusLabel) - .addComponent(progressBar)); - this.adjustWindowPositionBySavedState(); - this.setSaveWindowPositionOnClosing(); - - this.setName("Find All"); - this.setTitle("Find All"); - } - - private class FindButton extends AbstractAction { - private static final long serialVersionUID = 75954129199541874L; - - @Override - public void actionPerformed(ActionEvent event) { - tmp_thread = new Thread() { - public void run() { - if (findButton.getText().equals("Stop")) { - if (tmp_thread != null) - tmp_thread.interrupt(); - setStatus("Stopped."); - findButton.setText("Find"); - locked = false; - } else { - findButton.setText("Stop"); - classesList.clear(); - ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); - DecompilerSettings settings = configSaver.getDecompilerSettings(); - File inFile = mainWindow.getSelectedModel().getOpenedFile(); - boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() - .isFilterOutInnerClassEntries(); - try { - JarFile jfile = new JarFile(inFile); - Enumeration entLength = jfile.entries(); - initProgressBar(Collections.list(entLength).size()); - Enumeration ent = jfile.entries(); - while (ent.hasMoreElements() && findButton.getText().equals("Stop")) { - JarEntry entry = ent.nextElement(); - String name = entry.getName(); - setStatus(name); - if (filter && name.contains("$")) - continue; - if(locked || classname.isSelected()){ - locked = true; - if(search(entry.getName())) - addClassName(entry.getName()); - }else{ - if (entry.getName().endsWith(".class")) { - synchronized (settings) { - String internalName = StringUtilities.removeRight(entry.getName(), ".class"); - TypeReference type = Model.metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - StringWriter stringwriter = new StringWriter(); - DecompilationOptions decompilationOptions; - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled( - decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, - decompilationOptions); - if (search(stringwriter.toString())) - addClassName(entry.getName()); - } - } else { - - StringBuilder sb = new StringBuilder(); - long nonprintableCharactersCount = 0; - try (InputStreamReader inputStreamReader = new InputStreamReader( - jfile.getInputStream(entry)); - BufferedReader reader = new BufferedReader(inputStreamReader);) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - - for (byte nextByte : line.getBytes()) { - if (nextByte <= 0) { - nonprintableCharactersCount++; - } - } - - } - } - if (nonprintableCharactersCount < 5 && search(sb.toString())) - addClassName(entry.getName()); - } - } - } - setSearching(false); - if (findButton.getText().equals("Stop")) { - setStatus("Done."); - findButton.setText("Find"); - locked = false; - } - jfile.close(); - locked = false; - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - } - } - }; - tmp_thread.start(); - - } - - } - - private boolean search(String bulk) { - String a = textField.getText(); - String b = bulk; - if (regex.isSelected()) - return Pattern.matches(a, b); - if (wholew.isSelected()) - a = " " + a + " "; - if (!mcase.isSelected()) { - a = a.toLowerCase(); - b = b.toLowerCase(); - } - if (b.contains(a)) - return true; - return false; - } - - private void setHideOnEscapeButton() { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = 6846566740472934801L; - - @Override - public void actionPerformed(ActionEvent e) { - FindAllBox.this.setVisible(false); - } - }; - - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); - this.getRootPane().getActionMap().put("ESCAPE", escapeAction); - } - - private void adjustWindowPositionBySavedState() { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - - if (windowPosition.isSavedWindowPositionValid()) { - this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); - } - } - - private void setSaveWindowPositionOnClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowDeactivated(WindowEvent e) { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - windowPosition.readPositionFromDialog(FindAllBox.this); - } - }); - } - - public void showFindBox() { - this.setVisible(true); - this.textField.requestFocus(); - } - - public void hideFindBox() { - this.setVisible(false); - } - - public void setStatus(String text) { - if (text.length() > 25) { - this.statusLabel.setText("Searching in file: ..." + text.substring(text.length() - 25)); - } else { - this.statusLabel.setText("Searching in file: " + text); - } - - progressBar.setValue(progressBar.getValue() + 1); - } - - public void addClassName(String className) { - this.classesList.addElement(className); - } - - public void initProgressBar(Integer length) { - progressBar.setMaximum(length); - progressBar.setValue(0); - progressBar.setStringPainted(true); - } - - public boolean isSearching() { - return searching; - } - - public void setSearching(boolean searching) { - this.searching = searching; - } -} diff --git a/src/us/deathmarine/luyten/FindBox.java b/src/us/deathmarine/luyten/FindBox.java deleted file mode 100644 index 0c14256a..00000000 --- a/src/us/deathmarine/luyten/FindBox.java +++ /dev/null @@ -1,232 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rtextarea.SearchContext; -import org.fife.ui.rtextarea.SearchEngine; - -public class FindBox extends JDialog { - private static final long serialVersionUID = -4125409760166690462L; - - JCheckBox mcase; - JCheckBox regex; - JCheckBox wholew; - JCheckBox reverse; - JCheckBox wrap; - private JButton findButton; - JTextField textField; - private MainWindow mainWindow; - - public void showFindBox() { - this.setVisible(true); - this.textField.requestFocus(); - this.textField.selectAll(); - } - - public void hideFindBox() { - this.setVisible(false); - } - - public FindBox(final MainWindow mainWindow) { - this.mainWindow = mainWindow; - this.setDefaultCloseOperation(HIDE_ON_CLOSE); - this.setHideOnEscapeButton(); - - JLabel label = new JLabel("Find What:"); - textField = new JTextField(); - - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - textField.setText(pane.getSelectedText()); - } - mcase = new JCheckBox("Match Case"); - regex = new JCheckBox("Regex"); - wholew = new JCheckBox("Whole Words"); - reverse = new JCheckBox("Search Backwards"); - wrap = new JCheckBox("Wrap"); - - findButton = new JButton("Find"); - findButton.addActionListener(new FindButton()); - this.getRootPane().setDefaultButton(findButton); - - KeyStroke funcF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(funcF3, "FindNext"); - this.getRootPane().getActionMap().put("FindNext", new FindExploreAction(true)); - - KeyStroke sfuncF3 = KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF3, "FindPrevious"); - this.getRootPane().getActionMap().put("FindPrevious", new FindExploreAction(false)); - - mcase.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - regex.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - wholew.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - reverse.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - wrap.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - final Dimension center = new Dimension((int) (screenSize.width * 0.35), - Math.min((int) (screenSize.height * 0.20), 200)); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - this.setResizable(false); - - GroupLayout layout = new GroupLayout(getRootPane()); - getRootPane().setLayout(layout); - layout.setAutoCreateGaps(true); - layout.setAutoCreateContainerGaps(true); - - layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(label) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(textField) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(mcase) - .addComponent(wholew).addComponent(wrap)) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(regex) - .addComponent(reverse)))) - .addGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(findButton))); - - layout.linkSize(SwingConstants.HORIZONTAL, findButton); - layout.setVerticalGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(textField) - .addComponent(findButton)) - .addGroup(layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(mcase) - .addComponent(regex)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wholew) - .addComponent(reverse)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(wrap))))); - - this.adjustWindowPositionBySavedState(); - this.setSaveWindowPositionOnClosing(); - - this.setName("Find"); - this.setTitle("Find"); - this.setVisible(true); - } - - private class FindButton extends AbstractAction { - private static final long serialVersionUID = 75954129199541874L; - - @Override - public void actionPerformed(ActionEvent event) { - if (textField.getText().length() == 0) - return; - - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - - SearchContext context = new SearchContext(); - context.setSearchFor(textField.getText()); - context.setMatchCase(mcase.isSelected()); - context.setRegularExpression(regex.isSelected()); - context.setSearchForward(!reverse.isSelected()); - context.setWholeWord(wholew.isSelected()); - - if (!SearchEngine.find(pane, context).wasFound()) { - if (wrap.isSelected()) { - pane.setSelectionStart(0); - pane.setSelectionEnd(0); - } else { - mainWindow.getLabel().setText("Search Complete"); - } - } - } - - } - - private void setHideOnEscapeButton() { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = 5572504000935312338L; - - @Override - public void actionPerformed(ActionEvent e) { - FindBox.this.setVisible(false); - } - }; - - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE"); - this.getRootPane().getActionMap().put("ESCAPE", escapeAction); - } - - private void adjustWindowPositionBySavedState() { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - - if (windowPosition.isSavedWindowPositionValid()) { - this.setLocation(windowPosition.getWindowX(), windowPosition.getWindowY()); - } - } - - private void setSaveWindowPositionOnClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowDeactivated(WindowEvent e) { - WindowPosition windowPosition = ConfigSaver.getLoadedInstance().getFindWindowPosition(); - windowPosition.readPositionFromDialog(FindBox.this); - } - }); - } - - public void fireExploreAction(boolean direction) { - new FindExploreAction(direction).actionPerformed(null); - } - - class FindExploreAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = -4391670062679240573L; - boolean direction; - - public FindExploreAction(boolean forward) { - direction = forward; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (textField.getText().length() == 0) - return; - RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - SearchContext context = new SearchContext(); - context.setSearchFor(textField.getText()); - context.setMatchCase(mcase.isSelected()); - context.setRegularExpression(regex.isSelected()); - context.setSearchForward(direction); - context.setWholeWord(wholew.isSelected()); - - if (!SearchEngine.find(pane, context).wasFound()) { - if (wrap.isSelected()) { - pane.setSelectionStart(0); - pane.setSelectionEnd(0); - } else { - mainWindow.getLabel().setText("Search Complete"); - } - } - - } - - } -} diff --git a/src/us/deathmarine/luyten/JFontChooser.java b/src/us/deathmarine/luyten/JFontChooser.java deleted file mode 100644 index 1f57bed7..00000000 --- a/src/us/deathmarine/luyten/JFontChooser.java +++ /dev/null @@ -1,772 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Frame; -import java.awt.GraphicsEnvironment; -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ActionMap; -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.DefaultListCellRenderer; -import javax.swing.InputMap; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; -import javax.swing.border.Border; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; -import javax.swing.text.Position; - -/** - * The JFontChooser class is a swing component for font selection. - * This class has JFileChooser like APIs. The following code pops - * up a font chooser dialog. - * - **/ -public class JFontChooser extends JComponent { - /** - * - */ - private static final long serialVersionUID = 8856126034081661L; - // class variables - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int OK_OPTION = 0; - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int CANCEL_OPTION = 1; - /** - * Return value from showDialog(). - * - * @see #showDialog - **/ - public static final int ERROR_OPTION = -1; - private static final Font DEFAULT_SELECTED_FONT = new Font("Serif", Font.PLAIN, 12); - private static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 10); - private static final int[] FONT_STYLE_CODES = { Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC }; - private static final String[] DEFAULT_FONT_SIZE_STRINGS = { "8", "9", "10", "11", "12", "14", "16", "18", "20", - "22", "24", "26", "28", "36", "48", "72", }; - - // instance variables - protected int dialogResultValue = ERROR_OPTION; - - protected DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer(); - - private String[] fontStyleNames; - private String[] fontFamilyNames; - private String[] fontSizeStrings; - private JTextField fontFamilyTextField; - private JTextField fontStyleTextField; - private JTextField fontSizeTextField; - private JList fontNameList; - private JList fontStyleList; - private JList fontSizeList; - private JPanel fontNamePanel; - private JPanel fontStylePanel; - private JPanel fontSizePanel; - private JPanel samplePanel; - private JTextField sampleText; - - /** - * Constructs a JFontChooser object. - **/ - public JFontChooser() { - this(DEFAULT_FONT_SIZE_STRINGS); - } - - /** - * Constructs a JFontChooser object using the given font size - * array. - * - * @param fontSizeStrings - * the array of font size string. - **/ - public JFontChooser(String[] fontSizeStrings) { - if (fontSizeStrings == null) { - fontSizeStrings = DEFAULT_FONT_SIZE_STRINGS; - } - this.fontSizeStrings = fontSizeStrings; - - JPanel selectPanel = new JPanel(); - selectPanel.setLayout(new BoxLayout(selectPanel, BoxLayout.X_AXIS)); - selectPanel.add(getFontFamilyPanel()); - selectPanel.add(getFontStylePanel()); - selectPanel.add(getFontSizePanel()); - - JPanel contentsPanel = new JPanel(); - contentsPanel.setLayout(new GridLayout(2, 1)); - contentsPanel.add(selectPanel, BorderLayout.NORTH); - contentsPanel.add(getSamplePanel(), BorderLayout.CENTER); - - this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - this.add(contentsPanel); - this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - this.setSelectedFont(DEFAULT_SELECTED_FONT); - } - - public JTextField getFontFamilyTextField() { - if (fontFamilyTextField == null) { - fontFamilyTextField = new JTextField(); - fontFamilyTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontFamilyTextField)); - fontFamilyTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontFamilyList())); - fontFamilyTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontFamilyList())); - fontFamilyTextField.setFont(DEFAULT_FONT); - - } - return fontFamilyTextField; - } - - public JTextField getFontStyleTextField() { - if (fontStyleTextField == null) { - fontStyleTextField = new JTextField(); - fontStyleTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontStyleTextField)); - fontStyleTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontStyleList())); - fontStyleTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontStyleList())); - fontStyleTextField.setFont(DEFAULT_FONT); - - } - return fontStyleTextField; - } - - public JTextField getFontSizeTextField() { - if (fontSizeTextField == null) { - fontSizeTextField = new JTextField(); - fontSizeTextField.addFocusListener(new TextFieldFocusHandlerForTextSelection(fontSizeTextField)); - fontSizeTextField.addKeyListener(new TextFieldKeyHandlerForListSelectionUpDown(getFontSizeList())); - fontSizeTextField.getDocument() - .addDocumentListener(new ListSearchTextFieldDocumentHandler(getFontSizeList())); - fontSizeTextField.setFont(DEFAULT_FONT); - } - return fontSizeTextField; - } - - public JList getFontFamilyList() { - if (fontNameList == null) { - fontNameList = new JList(getFontFamilies()); - fontNameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontNameList.addListSelectionListener(new ListSelectionHandler(getFontFamilyTextField())); - fontNameList.setSelectedIndex(0); - - // Draw Fonts - fontNameList.setCellRenderer(new DefaultListCellRenderer() { - /** - * - */ - private static final long serialVersionUID = -6753380853569310954L; - - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - - if (value instanceof String) { - renderer.setText((String) value); - renderer.setFont(new Font((String) value, DEFAULT_FONT.getStyle(), DEFAULT_FONT.getSize() + 2)); - } else { - renderer.setText(""); - } - return renderer; - } - }); - fontNameList.setFocusable(false); - } - return fontNameList; - } - - public JList getFontStyleList() { - if (fontStyleList == null) { - fontStyleList = new JList(getFontStyleNames()); - fontStyleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontStyleList.addListSelectionListener(new ListSelectionHandler(getFontStyleTextField())); - fontStyleList.setSelectedIndex(0); - fontStyleList.setFocusable(false); - fontStyleList.setCellRenderer(new DefaultListCellRenderer() { - /** - * - */ - private static final long serialVersionUID = -3904668242514776943L; - - public Component getListCellRendererComponent(JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - - JLabel renderer = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, - isSelected, cellHasFocus); - - if (value instanceof String) { - renderer.setText((String) value); - renderer.setFont( - new Font(DEFAULT_FONT.getName(), FONT_STYLE_CODES[index], DEFAULT_FONT.getSize() + 2)); - } else { - renderer.setText(""); - } - return renderer; - } - }); - } - return fontStyleList; - } - - public JList getFontSizeList() { - if (fontSizeList == null) { - fontSizeList = new JList(this.fontSizeStrings); - fontSizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - fontSizeList.addListSelectionListener(new ListSelectionHandler(getFontSizeTextField())); - fontSizeList.setSelectedIndex(0); - fontSizeList.setFont(DEFAULT_FONT); - fontSizeList.setFocusable(false); - } - return fontSizeList; - } - - /** - * Get the family name of the selected font. - * - * @return the font family of the selected font. - * - * @see #setSelectedFontFamily - **/ - public String getSelectedFontFamily() { - String fontName = (String) getFontFamilyList().getSelectedValue(); - return fontName; - } - - /** - * Get the style of the selected font. - * - * @return the style of the selected font. Font.PLAIN, - * Font.BOLD, Font.ITALIC, - * Font.BOLD|Font.ITALIC - * - * @see java.awt.Font#PLAIN - * @see java.awt.Font#BOLD - * @see java.awt.Font#ITALIC - * @see #setSelectedFontStyle - **/ - public int getSelectedFontStyle() { - int index = getFontStyleList().getSelectedIndex(); - return FONT_STYLE_CODES[index]; - } - - /** - * Get the size of the selected font. - * - * @return the size of the selected font - * - * @see #setSelectedFontSize - **/ - public int getSelectedFontSize() { - int fontSize = 1; - String fontSizeString = getFontSizeTextField().getText(); - while (true) { - try { - fontSize = Integer.parseInt(fontSizeString); - break; - } catch (NumberFormatException e) { - fontSizeString = (String) getFontSizeList().getSelectedValue(); - getFontSizeTextField().setText(fontSizeString); - } - } - - return fontSize; - } - - /** - * Get the selected font. - * - * @return the selected font - * - * @see #setSelectedFont - * @see java.awt.Font - **/ - public Font getSelectedFont() { - Font font = new Font(getSelectedFontFamily(), getSelectedFontStyle(), getSelectedFontSize()); - return font; - } - - /** - * Set the family name of the selected font. - * - * @param name - * the family name of the selected font. - * - * @see getSelectedFontFamily - **/ - public void setSelectedFontFamily(String name) { - String[] names = getFontFamilies(); - for (int i = 0; i < names.length; i++) { - if (names[i].toLowerCase().equals(name.toLowerCase())) { - getFontFamilyList().setSelectedIndex(i); - break; - } - } - updateSampleFont(); - } - - /** - * Set the style of the selected font. - * - * @param style - * the size of the selected font. Font.PLAIN, - * Font.BOLD, Font.ITALIC, or - * Font.BOLD|Font.ITALIC. - * - * @see java.awt.Font#PLAIN - * @see java.awt.Font#BOLD - * @see java.awt.Font#ITALIC - * @see #getSelectedFontStyle - **/ - public void setSelectedFontStyle(int style) { - for (int i = 0; i < FONT_STYLE_CODES.length; i++) { - if (FONT_STYLE_CODES[i] == style) { - getFontStyleList().setSelectedIndex(i); - break; - } - } - updateSampleFont(); - } - - /** - * Set the size of the selected font. - * - * @param size - * the size of the selected font - * - * @see #getSelectedFontSize - **/ - public void setSelectedFontSize(int size) { - String sizeString = String.valueOf(size); - for (int i = 0; i < this.fontSizeStrings.length; i++) { - if (this.fontSizeStrings[i].equals(sizeString)) { - getFontSizeList().setSelectedIndex(i); - break; - } - } - getFontSizeTextField().setText(sizeString); - updateSampleFont(); - } - - /** - * Set the selected font. - * - * @param font - * the selected font - * - * @see #getSelectedFont - * @see java.awt.Font - **/ - public void setSelectedFont(Font font) { - setSelectedFontFamily(font.getFamily()); - setSelectedFontStyle(font.getStyle()); - setSelectedFontSize(font.getSize()); - } - - public String getVersionString() { - return ("Version"); - } - - /** - * Show font selection dialog. - * - * @param parent - * Dialog's Parent component. - * @return OK_OPTION, CANCEL_OPTION or ERROR_OPTION - * - * @see #OK_OPTION - * @see #CANCEL_OPTION - * @see #ERROR_OPTION - **/ - public int showDialog(Component parent) { - dialogResultValue = ERROR_OPTION; - JDialog dialog = createDialog(parent); - dialog.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - dialogResultValue = CANCEL_OPTION; - } - }); - - dialog.setVisible(true); - dialog.dispose(); - dialog = null; - - return dialogResultValue; - } - - protected class ListSelectionHandler implements ListSelectionListener { - private JTextComponent textComponent; - - ListSelectionHandler(JTextComponent textComponent) { - this.textComponent = textComponent; - } - - public void valueChanged(ListSelectionEvent e) { - if (e.getValueIsAdjusting() == false) { - JList list = (JList) e.getSource(); - String selectedValue = (String) list.getSelectedValue(); - - String oldValue = textComponent.getText(); - textComponent.setText(selectedValue); - if (!oldValue.equalsIgnoreCase(selectedValue)) { - textComponent.selectAll(); - textComponent.requestFocus(); - } - - updateSampleFont(); - } - } - } - - protected class TextFieldFocusHandlerForTextSelection extends FocusAdapter { - private JTextComponent textComponent; - - public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) { - this.textComponent = textComponent; - } - - public void focusGained(FocusEvent e) { - textComponent.selectAll(); - } - - public void focusLost(FocusEvent e) { - textComponent.select(0, 0); - updateSampleFont(); - } - } - - protected class TextFieldKeyHandlerForListSelectionUpDown extends KeyAdapter { - private JList targetList; - - public TextFieldKeyHandlerForListSelectionUpDown(JList list) { - this.targetList = list; - } - - public void keyPressed(KeyEvent e) { - int i = targetList.getSelectedIndex(); - switch (e.getKeyCode()) { - case KeyEvent.VK_UP: - i = targetList.getSelectedIndex() - 1; - if (i < 0) { - i = 0; - } - targetList.setSelectedIndex(i); - break; - case KeyEvent.VK_DOWN: - int listSize = targetList.getModel().getSize(); - i = targetList.getSelectedIndex() + 1; - if (i >= listSize) { - i = listSize - 1; - } - targetList.setSelectedIndex(i); - break; - default: - break; - } - } - } - - protected class ListSearchTextFieldDocumentHandler implements DocumentListener { - JList targetList; - - public ListSearchTextFieldDocumentHandler(JList targetList) { - this.targetList = targetList; - } - - public void insertUpdate(DocumentEvent e) { - update(e); - } - - public void removeUpdate(DocumentEvent e) { - update(e); - } - - public void changedUpdate(DocumentEvent e) { - update(e); - } - - private void update(DocumentEvent event) { - String newValue = ""; - try { - Document doc = event.getDocument(); - newValue = doc.getText(0, doc.getLength()); - } catch (BadLocationException e) { - Luyten.showExceptionDialog("Exception!", e); - } - - if (newValue.length() > 0) { - int index = targetList.getNextMatch(newValue, 0, Position.Bias.Forward); - if (index < 0) { - index = 0; - } - targetList.ensureIndexIsVisible(index); - - String matchedName = targetList.getModel().getElementAt(index).toString(); - if (newValue.equalsIgnoreCase(matchedName)) { - if (index != targetList.getSelectedIndex()) { - SwingUtilities.invokeLater(new ListSelector(index)); - } - } - } - } - - public class ListSelector implements Runnable { - private int index; - - public ListSelector(int index) { - this.index = index; - } - - public void run() { - targetList.setSelectedIndex(this.index); - } - } - } - - protected class DialogOKAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = 1618273732543947323L; - protected static final String ACTION_NAME = "OK"; - private JDialog dialog; - - protected DialogOKAction(JDialog dialog) { - this.dialog = dialog; - putValue(Action.DEFAULT, ACTION_NAME); - putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); - putValue(Action.NAME, (ACTION_NAME)); - } - - public void actionPerformed(ActionEvent e) { - dialogResultValue = OK_OPTION; - dialog.setVisible(false); - } - } - - protected class DialogCancelAction extends AbstractAction { - /** - * - */ - private static final long serialVersionUID = -4941763616565382601L; - protected static final String ACTION_NAME = "Cancel"; - private JDialog dialog; - - protected DialogCancelAction(JDialog dialog) { - this.dialog = dialog; - putValue(Action.DEFAULT, ACTION_NAME); - putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); - putValue(Action.NAME, (ACTION_NAME)); - } - - public void actionPerformed(ActionEvent e) { - dialogResultValue = CANCEL_OPTION; - dialog.setVisible(false); - } - } - - protected JDialog createDialog(Component parent) { - Frame frame = parent instanceof Frame ? (Frame) parent - : (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); - JDialog dialog = new JDialog(frame, ("Select Font"), true); - - Action okAction = new DialogOKAction(dialog); - Action cancelAction = new DialogCancelAction(dialog); - - JButton okButton = new JButton(okAction); - okButton.setFont(DEFAULT_FONT); - JButton cancelButton = new JButton(cancelAction); - cancelButton.setFont(DEFAULT_FONT); - - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new GridLayout(2, 1)); - buttonsPanel.add(okButton); - buttonsPanel.add(cancelButton); - buttonsPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 10, 10)); - - ActionMap actionMap = buttonsPanel.getActionMap(); - actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction); - actionMap.put(okAction.getValue(Action.DEFAULT), okAction); - InputMap inputMap = buttonsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), cancelAction.getValue(Action.DEFAULT)); - inputMap.put(KeyStroke.getKeyStroke("ENTER"), okAction.getValue(Action.DEFAULT)); - - JPanel dialogEastPanel = new JPanel(); - dialogEastPanel.setLayout(new BorderLayout()); - dialogEastPanel.add(buttonsPanel, BorderLayout.NORTH); - - dialog.getContentPane().add(this, BorderLayout.CENTER); - dialog.getContentPane().add(dialogEastPanel, BorderLayout.EAST); - dialog.pack(); - dialog.setLocationRelativeTo(frame); - return dialog; - } - - protected void updateSampleFont() { - Font font = getSelectedFont(); - getSampleTextField().setFont(font); - } - - protected JPanel getFontFamilyPanel() { - if (fontNamePanel == null) { - fontNamePanel = new JPanel(); - fontNamePanel.setLayout(new BorderLayout()); - fontNamePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - fontNamePanel.setPreferredSize(new Dimension(180, 130)); - - JScrollPane scrollPane = new JScrollPane(getFontFamilyList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontFamilyTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Name")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontFamilyTextField()); - label.setDisplayedMnemonic('F'); - - fontNamePanel.add(label, BorderLayout.NORTH); - fontNamePanel.add(p, BorderLayout.CENTER); - - } - return fontNamePanel; - } - - protected JPanel getFontStylePanel() { - if (fontStylePanel == null) { - fontStylePanel = new JPanel(); - fontStylePanel.setLayout(new BorderLayout()); - fontStylePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - fontStylePanel.setPreferredSize(new Dimension(140, 130)); - - JScrollPane scrollPane = new JScrollPane(getFontStyleList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontStyleTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Style")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontStyleTextField()); - label.setDisplayedMnemonic('Y'); - - fontStylePanel.add(label, BorderLayout.NORTH); - fontStylePanel.add(p, BorderLayout.CENTER); - } - return fontStylePanel; - } - - protected JPanel getFontSizePanel() { - if (fontSizePanel == null) { - fontSizePanel = new JPanel(); - fontSizePanel.setLayout(new BorderLayout()); - fontSizePanel.setPreferredSize(new Dimension(70, 130)); - fontSizePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - - JScrollPane scrollPane = new JScrollPane(getFontSizeList()); - scrollPane.getVerticalScrollBar().setFocusable(false); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - - JPanel p = new JPanel(); - p.setLayout(new BorderLayout()); - p.add(getFontSizeTextField(), BorderLayout.NORTH); - p.add(scrollPane, BorderLayout.CENTER); - - JLabel label = new JLabel(("Font Size")); - label.setHorizontalAlignment(JLabel.LEFT); - label.setHorizontalTextPosition(JLabel.LEFT); - label.setLabelFor(getFontSizeTextField()); - label.setDisplayedMnemonic('S'); - - fontSizePanel.add(label, BorderLayout.NORTH); - fontSizePanel.add(p, BorderLayout.CENTER); - } - return fontSizePanel; - } - - protected JPanel getSamplePanel() { - if (samplePanel == null) { - Border titledBorder = BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), ("Sample")); - Border empty = BorderFactory.createEmptyBorder(5, 10, 10, 10); - Border border = BorderFactory.createCompoundBorder(titledBorder, empty); - - samplePanel = new JPanel(); - samplePanel.setLayout(new BorderLayout()); - samplePanel.setBorder(border); - - samplePanel.add(getSampleTextField(), BorderLayout.CENTER); - } - return samplePanel; - } - - protected JTextField getSampleTextField() { - if (sampleText == null) { - Border lowered = BorderFactory.createLoweredBevelBorder(); - - sampleText = new JTextField(("AaBbYyZz")); - sampleText.setHorizontalAlignment(JTextField.CENTER); - sampleText.setBorder(lowered); - sampleText.setPreferredSize(new Dimension(300, 100)); - } - return sampleText; - } - - protected String[] getFontFamilies() { - if (fontFamilyNames == null) { - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - fontFamilyNames = env.getAvailableFontFamilyNames(); - } - return fontFamilyNames; - } - - protected String[] getFontStyleNames() { - if (fontStyleNames == null) { - int i = 0; - fontStyleNames = new String[4]; - fontStyleNames[i++] = ("Plain"); - fontStyleNames[i++] = ("Bold"); - fontStyleNames[i++] = ("Italic"); - fontStyleNames[i++] = ("BoldItalic"); - } - return fontStyleNames; - } -} \ No newline at end of file diff --git a/src/us/deathmarine/luyten/JarEntryFilter.java b/src/us/deathmarine/luyten/JarEntryFilter.java deleted file mode 100644 index fc8a292a..00000000 --- a/src/us/deathmarine/luyten/JarEntryFilter.java +++ /dev/null @@ -1,82 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -public class JarEntryFilter { - - private JarFile jfile; - - public JarEntryFilter() { - } - - public JarEntryFilter(JarFile jfile) { - this.jfile = jfile; - } - - public List getAllEntriesFromJar() { - List mass = new ArrayList<>(); - Enumeration entries = jfile.entries(); - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (!e.isDirectory()) { - mass.add(e.getName()); - } - } - return mass; - } - - public List getEntriesWithoutInnerClasses() { - List mass = new ArrayList<>(); - Enumeration entries = jfile.entries(); - Set possibleInnerClasses = new HashSet(); - Set baseClasses = new HashSet(); - - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (!e.isDirectory()) { - String entryName = e.getName(); - - if (entryName != null && entryName.trim().length() > 0) { - entryName = entryName.trim(); - - if (!entryName.endsWith(".class")) { - mass.add(entryName); - - // com/acme/Model$16.class - } else if (entryName.matches(".*[^(/|\\\\)]+\\$[^(/|\\\\)]+$")) { - possibleInnerClasses.add(entryName); - - } else { - baseClasses.add(entryName); - mass.add(entryName); - } - } - } - } - - // keep Badly$Named but not inner classes - for (String inner : possibleInnerClasses) { - - // com/acme/Connection$Conn$1.class -> com/acme/Connection - String innerWithoutTail = inner.replaceAll("\\$[^(/|\\\\)]+\\.class$", ""); - if (!baseClasses.contains(innerWithoutTail + ".class")) { - mass.add(inner); - } - } - return mass; - } - - public JarFile getJfile() { - return jfile; - } - - public void setJfile(JarFile jfile) { - this.jfile = jfile; - } -} diff --git a/src/us/deathmarine/luyten/LinkProvider.java b/src/us/deathmarine/luyten/LinkProvider.java deleted file mode 100644 index e6911e9f..00000000 --- a/src/us/deathmarine/luyten/LinkProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.Map; -import java.util.Set; - -public interface LinkProvider { - - public void generateContent(); - - public String getTextContent(); - - public void processLinks(); - - public Map getDefinitionToSelectionMap(); - - public Map> getReferenceToSelectionsMap(); - - public boolean isLinkNavigable(String uniqueStr); - - public String getLinkDescription(String uniqueStr); - -} diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java deleted file mode 100644 index 6c5bd7b9..00000000 --- a/src/us/deathmarine/luyten/Luyten.java +++ /dev/null @@ -1,276 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.*; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URI; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicReference; -import java.util.List; -import java.util.ArrayList; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.border.BevelBorder; -import javax.swing.border.CompoundBorder; -import javax.swing.text.DefaultEditorKit; - -/** - * Starter, the main class - */ -public class Luyten { - - private static final AtomicReference mainWindowRef = new AtomicReference<>(); - private static final List pendingFiles = new ArrayList<>(); - private static ServerSocket lockSocket = null; - - public static void main(final String[] args) { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - try { - if (lockSocket != null) { - lockSocket.close(); - } - } catch (IOException e) { - } - } - })); - - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - e.printStackTrace(); - } - - // for TotalCommander External Viewer setting: - // javaw -jar "c:\Program Files\Luyten\luyten.jar" - // (TC will not complain about temporary file when opening .class from - // .zip or .jar) - final File fileFromCommandLine = getFileFromCommandLine(args); - - try { - launchMainInstance(fileFromCommandLine); - } catch (Exception e) { - // Instance already exists. Open new file in running instance - try { - Socket socket = new Socket("localhost", 3456); - DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); - dos.writeUTF(args[0]); - dos.flush(); - dos.close(); - socket.close(); - } catch (IOException ex) { - showExceptionDialog("Exception", e); - } - } - } - - private static void launchMainInstance(final File fileFromCommandLine) throws IOException { - lockSocket = new ServerSocket(3456); - launchSession(fileFromCommandLine); - new Thread(new Runnable() { - @Override - public void run() { - launchServer(); - } - }).start(); - } - - private static void launchSession(final File fileFromCommandLine) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { - // Already set - so add the files to open - addToPendingFiles(fileFromCommandLine); - } - processPendingFiles(); - mainWindowRef.get().setVisible(true); - } - }); - } - - private static void launchServer() { - try { // Server - while (true) { - Socket socket = lockSocket.accept(); - DataInputStream dis = new DataInputStream(socket.getInputStream()); - addToPendingFiles(getFileFromCommandLine(dis.readUTF())); - processPendingFiles(); - dis.close(); - socket.close(); - } - } catch (IOException e) { // Client - showExceptionDialog("Exception", e); - } - } - - // Private function which processes all pending files - synchronized on the - // list of pending files - public static void processPendingFiles() { - final MainWindow mainWindow = mainWindowRef.get(); - if (mainWindow != null) { - synchronized (pendingFiles) { - for (File f : pendingFiles) { - mainWindow.loadNewFile(f); - } - pendingFiles.clear(); - } - } - } - - // Function which opens the given file in the instance, if it's running - - // and if not, it processes the files - public static void addToPendingFiles(File fileToOpen) { - synchronized (pendingFiles) { - if (fileToOpen != null) { - pendingFiles.add(fileToOpen); - } - } - } - - // Function which exits the application if it's running - public static void quitInstance() { - final MainWindow mainWindow = mainWindowRef.get(); - if (mainWindow != null) { - mainWindow.onExitMenu(); - } - } - - public static File getFileFromCommandLine(String... args) { - File fileFromCommandLine = null; - try { - if (args.length > 0) { - String realFileName = new File(args[0]).getCanonicalPath(); - fileFromCommandLine = new File(realFileName); - } - } catch (Exception e) { - e.printStackTrace(); - } - return fileFromCommandLine; - } - - public static String getVersion() { - String result = ""; - try { - String line; - BufferedReader br = new BufferedReader(new InputStreamReader( - ClassLoader.getSystemResourceAsStream("META-INF/maven/us.deathmarine/luyten/pom.properties"))); - while ((line = br.readLine()) != null) { - if (line.contains("version")) - result = line.split("=")[1]; - } - br.close(); - } catch (Exception e) { - return result; - } - return result; - - } - - /** - * Method allows for users to copy the stacktrace for reporting any issues. - * Add Cool Hyperlink Enhanced for mouse users. - * - * @param message - * @param e - */ - public static void showExceptionDialog(String message, Exception e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - String stacktrace = sw.toString(); - try { - sw.close(); - pw.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - System.out.println(stacktrace); - - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - if (message.contains("\n")) { - for (String s : message.split("\n")) { - pane.add(new JLabel(s)); - } - } else { - pane.add(new JLabel(message)); - } - pane.add(new JLabel(" \n")); // Whitespace - final JTextArea exception = new JTextArea(25, 100); - exception.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 10)); - exception.setText(stacktrace); - exception.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - new JPopupMenu() { - { - JMenuItem menuitem = new JMenuItem("Select All"); - menuitem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - exception.requestFocus(); - exception.selectAll(); - } - }); - this.add(menuitem); - menuitem = new JMenuItem("Copy"); - menuitem.addActionListener(new DefaultEditorKit.CopyAction()); - this.add(menuitem); - } - - private static final long serialVersionUID = 562054483562666832L; - }.show(e.getComponent(), e.getX(), e.getY()); - } - } - }); - JScrollPane scroll = new JScrollPane(exception); - scroll.setBorder(new CompoundBorder(BorderFactory.createTitledBorder("Stacktrace"), - new BevelBorder(BevelBorder.LOWERED))); - pane.add(scroll); - final String issue = "https://github.com/deathmarine/Luyten/issues"; - final JLabel link = new JLabel("Submit to " + issue + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(new URI(issue)); - } catch (Exception e1) { - e1.printStackTrace(); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - link.setText("Submit to " + issue + ""); - } - - @Override - public void mouseExited(MouseEvent e) { - link.setText("Submit to " + issue + ""); - } - }); - pane.add(link); - JOptionPane.showMessageDialog(null, pane, "Error!", JOptionPane.ERROR_MESSAGE); - } -} diff --git a/src/us/deathmarine/luyten/LuytenOsx.java b/src/us/deathmarine/luyten/LuytenOsx.java deleted file mode 100644 index 3ba653d2..00000000 --- a/src/us/deathmarine/luyten/LuytenOsx.java +++ /dev/null @@ -1,31 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.File; -import com.apple.eawt.Application; -import com.apple.eawt.ApplicationAdapter; -import com.apple.eawt.ApplicationEvent; - -/** - * An OS X-specific initialization method for dragging/dropping - */ -public class LuytenOsx extends Luyten { - public static void main(String[] args) { - // Add an adapter as the handler to a new instance of the application - // class - @SuppressWarnings("deprecation") - Application app = new Application(); - app.addApplicationListener(new ApplicationAdapter() { - public void handleOpenFile(ApplicationEvent e) { - Luyten.addToPendingFiles(new File(e.getFilename())); - Luyten.processPendingFiles(); - } - - public void handleQuit(ApplicationEvent e) { - Luyten.quitInstance(); - } - }); - - // Call the superclass's main function - Luyten.main(args); - } -} diff --git a/src/us/deathmarine/luyten/LuytenPreferences.java b/src/us/deathmarine/luyten/LuytenPreferences.java deleted file mode 100644 index 341e4408..00000000 --- a/src/us/deathmarine/luyten/LuytenPreferences.java +++ /dev/null @@ -1,87 +0,0 @@ -package us.deathmarine.luyten; - -/** - * Do not instantiate this class, get the instance from ConfigSaver. All - * not-static fields will be saved automatically named by the field's java - * variable name. (Watch for collisions with existing IDs defined in - * ConfigSaver.) Only String, boolean and int fields are supported. Write - * default values into the field declarations. - */ -public class LuytenPreferences { - public static final String THEME_XML_PATH = "/org/fife/ui/rsyntaxtextarea/themes/"; - public static final String DEFAULT_THEME_XML = "eclipse.xml"; - - private String themeXml = DEFAULT_THEME_XML; - private String fileOpenCurrentDirectory = ""; - private String fileSaveCurrentDirectory = ""; - private int font_size = 10; - - private boolean isPackageExplorerStyle = true; - private boolean isFilterOutInnerClassEntries = true; - private boolean isSingleClickOpenEnabled = true; - private boolean isExitByEscEnabled = false; - - public String getThemeXml() { - return themeXml; - } - - public void setThemeXml(String themeXml) { - this.themeXml = themeXml; - } - - public String getFileOpenCurrentDirectory() { - return fileOpenCurrentDirectory; - } - - public void setFileOpenCurrentDirectory(String fileOpenCurrentDirectory) { - this.fileOpenCurrentDirectory = fileOpenCurrentDirectory; - } - - public String getFileSaveCurrentDirectory() { - return fileSaveCurrentDirectory; - } - - public void setFileSaveCurrentDirectory(String fileSaveCurrentDirectory) { - this.fileSaveCurrentDirectory = fileSaveCurrentDirectory; - } - - public boolean isPackageExplorerStyle() { - return isPackageExplorerStyle; - } - - public void setPackageExplorerStyle(boolean isPackageExplorerStyle) { - this.isPackageExplorerStyle = isPackageExplorerStyle; - } - - public boolean isFilterOutInnerClassEntries() { - return isFilterOutInnerClassEntries; - } - - public void setFilterOutInnerClassEntries(boolean isFilterOutInnerClassEntries) { - this.isFilterOutInnerClassEntries = isFilterOutInnerClassEntries; - } - - public boolean isSingleClickOpenEnabled() { - return isSingleClickOpenEnabled; - } - - public void setSingleClickOpenEnabled(boolean isSingleClickOpenEnabled) { - this.isSingleClickOpenEnabled = isSingleClickOpenEnabled; - } - - public boolean isExitByEscEnabled() { - return isExitByEscEnabled; - } - - public void setExitByEscEnabled(boolean isExitByEscEnabled) { - this.isExitByEscEnabled = isExitByEscEnabled; - } - - public int getFont_size() { - return font_size; - } - - public void setFont_size(int font_size) { - this.font_size = font_size; - } -} diff --git a/src/us/deathmarine/luyten/LuytenTypeLoader.java b/src/us/deathmarine/luyten/LuytenTypeLoader.java deleted file mode 100644 index 1198ac01..00000000 --- a/src/us/deathmarine/luyten/LuytenTypeLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package us.deathmarine.luyten; - -import com.strobel.assembler.InputTypeLoader; -import com.strobel.assembler.metadata.Buffer; -import com.strobel.assembler.metadata.ITypeLoader; - -import java.util.ArrayList; -import java.util.List; - -public final class LuytenTypeLoader implements ITypeLoader { - private final List _typeLoaders; - - public LuytenTypeLoader() { - _typeLoaders = new ArrayList(); - _typeLoaders.add(new InputTypeLoader()); - } - - public final List getTypeLoaders() { - return _typeLoaders; - } - - @Override - public boolean tryLoadType(final String internalName, final Buffer buffer) { - for (final ITypeLoader typeLoader : _typeLoaders) { - if (typeLoader.tryLoadType(internalName, buffer)) { - return true; - } - - buffer.reset(); - } - - return false; - } -} \ No newline at end of file diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java deleted file mode 100644 index bd108c70..00000000 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ /dev/null @@ -1,677 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Cursor; -import java.awt.Desktop; -import java.awt.Font; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.ListIterator; -import java.util.Map; - -import javax.swing.AbstractAction; -import javax.swing.AbstractButton; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.ButtonModel; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JTabbedPane; -import javax.swing.KeyStroke; -import javax.swing.text.DefaultEditorKit; - -import com.strobel.Procyon; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.languages.Language; -import com.strobel.decompiler.languages.Languages; - -/** - * Main menu (only MainWindow should be called from here) - */ -public class MainMenuBar extends JMenuBar { - private static final long serialVersionUID = -7949855817172562075L; - private final MainWindow mainWindow; - private final Map languageLookup = new HashMap(); - - private JMenu recentFiles; - private JMenuItem clearRecentFiles; - private JCheckBoxMenuItem flattenSwitchBlocks; - private JCheckBoxMenuItem forceExplicitImports; - private JCheckBoxMenuItem forceExplicitTypes; - private JCheckBoxMenuItem showSyntheticMembers; - private JCheckBoxMenuItem excludeNestedTypes; - private JCheckBoxMenuItem retainRedundantCasts; - private JCheckBoxMenuItem unicodeReplacement; - private JCheckBoxMenuItem debugLineNumbers; - private JCheckBoxMenuItem showDebugInfo; - private JCheckBoxMenuItem bytecodeLineNumbers; - private JRadioButtonMenuItem java; - private JRadioButtonMenuItem bytecode; - private JRadioButtonMenuItem bytecodeAST; - private ButtonGroup languagesGroup; - private ButtonGroup themesGroup; - private JCheckBoxMenuItem packageExplorerStyle; - private JCheckBoxMenuItem filterOutInnerClassEntries; - private JCheckBoxMenuItem singleClickOpenEnabled; - private JCheckBoxMenuItem exitByEscEnabled; - private DecompilerSettings settings; - private LuytenPreferences luytenPrefs; - - public MainMenuBar(MainWindow mainWnd) { - this.mainWindow = mainWnd; - final ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); - settings = configSaver.getDecompilerSettings(); - luytenPrefs = configSaver.getLuytenPreferences(); - - final JMenu fileMenu = new JMenu("File"); - fileMenu.add(new JMenuItem("...")); - this.add(fileMenu); - final JMenu editMenu = new JMenu("Edit"); - editMenu.add(new JMenuItem("...")); - this.add(editMenu); - final JMenu themesMenu = new JMenu("Themes"); - themesMenu.add(new JMenuItem("...")); - this.add(themesMenu); - final JMenu operationMenu = new JMenu("Operation"); - operationMenu.add(new JMenuItem("...")); - this.add(operationMenu); - final JMenu settingsMenu = new JMenu("Settings"); - settingsMenu.add(new JMenuItem("...")); - this.add(settingsMenu); - final JMenu helpMenu = new JMenu("Help"); - helpMenu.add(new JMenuItem("...")); - this.add(helpMenu); - - // start quicker - new Thread() { - public void run() { - try { - // build menu later - buildFileMenu(fileMenu); - refreshMenuPopup(fileMenu); - - buildEditMenu(editMenu); - refreshMenuPopup(editMenu); - - buildThemesMenu(themesMenu); - refreshMenuPopup(themesMenu); - - buildOperationMenu(operationMenu); - refreshMenuPopup(operationMenu); - - buildSettingsMenu(settingsMenu, configSaver); - refreshMenuPopup(settingsMenu); - - buildHelpMenu(helpMenu); - refreshMenuPopup(helpMenu); - - updateRecentFiles(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - // refresh currently opened menu - // (if user selected a menu before it was ready) - private void refreshMenuPopup(JMenu menu) { - try { - if (menu.isPopupMenuVisible()) { - menu.getPopupMenu().setVisible(false); - menu.getPopupMenu().setVisible(true); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }.start(); - } - - public void updateRecentFiles() { - if (RecentFiles.paths.isEmpty()) { - recentFiles.setEnabled(false); - clearRecentFiles.setEnabled(false); - return; - } else { - recentFiles.setEnabled(true); - clearRecentFiles.setEnabled(true); - } - - recentFiles.removeAll(); - ListIterator li = RecentFiles.paths.listIterator(RecentFiles.paths.size()); - boolean rfSaveNeeded = false; - - while (li.hasPrevious()) { - String path = li.previous(); - final File file = new File(path); - - if (!file.exists()) { - rfSaveNeeded = true; - continue; - } - - JMenuItem menuItem = new JMenuItem(path); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.loadNewFile(file); - } - }); - recentFiles.add(menuItem); - } - - if (rfSaveNeeded) RecentFiles.save(); - } - - private void buildFileMenu(final JMenu fileMenu) { - fileMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Open File..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onOpenFileMenu(); - } - }); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - menuItem = new JMenuItem("Close File"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JTabbedPane house = mainWindow.getSelectedModel().house; - - if (e.getModifiers() != 2 || house.getTabCount() == 0) - mainWindow.onCloseFileMenu(); - else { - mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); - } - } - }); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - menuItem = new JMenuItem("Save As..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSaveAsMenu(); - } - }); - fileMenu.add(menuItem); - - menuItem = new JMenuItem("Save All..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSaveAllMenu(); - } - }); - fileMenu.add(menuItem); - fileMenu.addSeparator(); - - recentFiles = new JMenu("Recent Files"); - fileMenu.add(recentFiles); - - clearRecentFiles = new JMenuItem("Clear Recent Files"); - clearRecentFiles.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - RecentFiles.paths.clear(); - RecentFiles.save(); - updateRecentFiles(); - } - }); - fileMenu.add(clearRecentFiles); - - fileMenu.addSeparator(); - - // Only add the exit command for non-OS X. OS X handles its close - // automatically - if (!Boolean.getBoolean("apple.laf.useScreenMenuBar")) { - menuItem = new JMenuItem("Exit"); - menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onExitMenu(); - } - }); - fileMenu.add(menuItem); - } - } - - private void buildEditMenu(JMenu editMenu) { - editMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Cut"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.setEnabled(false); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Copy"); - menuItem.addActionListener(new DefaultEditorKit.CopyAction()); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Paste"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.setEnabled(false); - editMenu.add(menuItem); - - editMenu.addSeparator(); - - menuItem = new JMenuItem("Select All"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onSelectAllMenu(); - } - }); - editMenu.add(menuItem); - editMenu.addSeparator(); - - menuItem = new JMenuItem("Find..."); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onFindMenu(); - } - }); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find Next"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(true); - } - }); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find Previous"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if(mainWindow.findBox != null) mainWindow.findBox.fireExploreAction(false); - } - }); - editMenu.add(menuItem); - - menuItem = new JMenuItem("Find All"); - menuItem.setAccelerator( - KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onFindAllMenu(); - - } - }); - editMenu.add(menuItem); - } - - private void buildThemesMenu(JMenu themesMenu) { - themesMenu.removeAll(); - themesGroup = new ButtonGroup(); - JRadioButtonMenuItem a = new JRadioButtonMenuItem(new ThemeAction("Default", "default.xml")); - a.setSelected("default.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Default-Alt", "default-alt.xml")); - a.setSelected("default-alt.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Dark", "dark.xml")); - a.setSelected("dark.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Eclipse", "eclipse.xml")); - a.setSelected("eclipse.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("Visual Studio", "vs.xml")); - a.setSelected("vs.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - - a = new JRadioButtonMenuItem(new ThemeAction("IntelliJ", "idea.xml")); - a.setSelected("idea.xml".equals(luytenPrefs.getThemeXml())); - themesGroup.add(a); - themesMenu.add(a); - } - - private void buildOperationMenu(JMenu operationMenu) { - operationMenu.removeAll(); - packageExplorerStyle = new JCheckBoxMenuItem("Package Explorer Style"); - packageExplorerStyle.setSelected(luytenPrefs.isPackageExplorerStyle()); - packageExplorerStyle.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setPackageExplorerStyle(packageExplorerStyle.isSelected()); - mainWindow.onTreeSettingsChanged(); - } - }); - operationMenu.add(packageExplorerStyle); - - filterOutInnerClassEntries = new JCheckBoxMenuItem("Filter Out Inner Class Entries"); - filterOutInnerClassEntries.setSelected(luytenPrefs.isFilterOutInnerClassEntries()); - filterOutInnerClassEntries.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setFilterOutInnerClassEntries(filterOutInnerClassEntries.isSelected()); - mainWindow.onTreeSettingsChanged(); - } - }); - operationMenu.add(filterOutInnerClassEntries); - - singleClickOpenEnabled = new JCheckBoxMenuItem("Single Click Open"); - singleClickOpenEnabled.setSelected(luytenPrefs.isSingleClickOpenEnabled()); - singleClickOpenEnabled.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setSingleClickOpenEnabled(singleClickOpenEnabled.isSelected()); - } - }); - operationMenu.add(singleClickOpenEnabled); - - exitByEscEnabled = new JCheckBoxMenuItem("Exit By Esc"); - exitByEscEnabled.setSelected(luytenPrefs.isExitByEscEnabled()); - exitByEscEnabled.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setExitByEscEnabled(exitByEscEnabled.isSelected()); - } - }); - operationMenu.add(exitByEscEnabled); - } - - private void buildSettingsMenu(JMenu settingsMenu, ConfigSaver configSaver) { - settingsMenu.removeAll(); - ActionListener settingsChanged = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - new Thread() { - @Override - public void run() { - populateSettingsFromSettingsMenu(); - mainWindow.onSettingsChanged(); - } - }.start(); - } - }; - flattenSwitchBlocks = new JCheckBoxMenuItem("Flatten Switch Blocks"); - flattenSwitchBlocks.setSelected(settings.getFlattenSwitchBlocks()); - flattenSwitchBlocks.addActionListener(settingsChanged); - settingsMenu.add(flattenSwitchBlocks); - - forceExplicitImports = new JCheckBoxMenuItem("Force Explicit Imports"); - forceExplicitImports.setSelected(settings.getForceExplicitImports()); - forceExplicitImports.addActionListener(settingsChanged); - settingsMenu.add(forceExplicitImports); - - forceExplicitTypes = new JCheckBoxMenuItem("Force Explicit Types"); - forceExplicitTypes.setSelected(settings.getForceExplicitTypeArguments()); - forceExplicitTypes.addActionListener(settingsChanged); - settingsMenu.add(forceExplicitTypes); - - showSyntheticMembers = new JCheckBoxMenuItem("Show Synthetic Members"); - showSyntheticMembers.setSelected(settings.getShowSyntheticMembers()); - showSyntheticMembers.addActionListener(settingsChanged); - settingsMenu.add(showSyntheticMembers); - - excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types"); - excludeNestedTypes.setSelected(settings.getExcludeNestedTypes()); - excludeNestedTypes.addActionListener(settingsChanged); - settingsMenu.add(excludeNestedTypes); - - retainRedundantCasts = new JCheckBoxMenuItem("Retain Redundant Casts"); - retainRedundantCasts.setSelected(settings.getRetainRedundantCasts()); - retainRedundantCasts.addActionListener(settingsChanged); - settingsMenu.add(retainRedundantCasts); - - unicodeReplacement = new JCheckBoxMenuItem("Enable Unicode Replacement"); - unicodeReplacement.setSelected(settings.isUnicodeOutputEnabled()); - unicodeReplacement.addActionListener(settingsChanged); - settingsMenu.add(unicodeReplacement); - - debugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers"); - debugLineNumbers.setSelected(settings.getShowDebugLineNumbers()); - debugLineNumbers.addActionListener(settingsChanged); - settingsMenu.add(debugLineNumbers); - - JMenu debugSettingsMenu = new JMenu("Debug Settings"); - showDebugInfo = new JCheckBoxMenuItem("Include Error Diagnostics"); - showDebugInfo.setSelected(settings.getIncludeErrorDiagnostics()); - showDebugInfo.addActionListener(settingsChanged); - - debugSettingsMenu.add(showDebugInfo); - settingsMenu.add(debugSettingsMenu); - settingsMenu.addSeparator(); - - languageLookup.put(Languages.java().getName(), Languages.java()); - languageLookup.put(Languages.bytecode().getName(), Languages.bytecode()); - languageLookup.put(Languages.bytecodeAst().getName(), Languages.bytecodeAst()); - - languagesGroup = new ButtonGroup(); - java = new JRadioButtonMenuItem(Languages.java().getName()); - java.getModel().setActionCommand(Languages.java().getName()); - java.setSelected(Languages.java().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(java); - settingsMenu.add(java); - bytecode = new JRadioButtonMenuItem(Languages.bytecode().getName()); - bytecode.getModel().setActionCommand(Languages.bytecode().getName()); - bytecode.setSelected(Languages.bytecode().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(bytecode); - settingsMenu.add(bytecode); - bytecodeAST = new JRadioButtonMenuItem(Languages.bytecodeAst().getName()); - bytecodeAST.getModel().setActionCommand(Languages.bytecodeAst().getName()); - bytecodeAST.setSelected(Languages.bytecodeAst().getName().equals(settings.getLanguage().getName())); - languagesGroup.add(bytecodeAST); - settingsMenu.add(bytecodeAST); - - JMenu debugLanguagesMenu = new JMenu("Debug Languages"); - for (final Language language : Languages.debug()) { - final JRadioButtonMenuItem m = new JRadioButtonMenuItem(language.getName()); - m.getModel().setActionCommand(language.getName()); - m.setSelected(language.getName().equals(settings.getLanguage().getName())); - languagesGroup.add(m); - debugLanguagesMenu.add(m); - languageLookup.put(language.getName(), language); - } - for (AbstractButton button : Collections.list(languagesGroup.getElements())) { - button.addActionListener(settingsChanged); - } - settingsMenu.add(debugLanguagesMenu); - - bytecodeLineNumbers = new JCheckBoxMenuItem("Show Line Numbers In Bytecode"); - bytecodeLineNumbers.setSelected(settings.getIncludeLineNumbersInBytecode()); - bytecodeLineNumbers.addActionListener(settingsChanged); - settingsMenu.add(bytecodeLineNumbers); - } - - private void buildHelpMenu(JMenu helpMenu) { - helpMenu.removeAll(); - JMenuItem menuItem = new JMenuItem("Legal"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onLegalMenu(); - } - }); - helpMenu.add(menuItem); - JMenu menuDebug = new JMenu("Debug"); - menuItem = new JMenuItem("List JVM Classes"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mainWindow.onListLoadedClasses(); - } - }); - menuDebug.add(menuItem); - helpMenu.add(menuDebug); - menuItem = new JMenuItem("About"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - JPanel pane = new JPanel(); - pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); - JLabel title = new JLabel("Luyten " + Luyten.getVersion()); - title.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18)); - pane.add(title); - pane.add(new JLabel("by Deathmarine")); - String project = "https://github.com/deathmarine/Luyten/"; - JLabel link = new JLabel("" + project + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(project, link)); - pane.add(link); - pane.add(new JLabel("Contributions By:")); - pane.add(new JLabel("zerdei, toonetown, dstmath")); - pane.add(new JLabel("virustotalop, xtrafrancyz,")); - pane.add(new JLabel("mbax, quitten, mstrobel,")); - pane.add(new JLabel("FisheyLP, and Syquel")); - pane.add(new JLabel(" ")); - pane.add(new JLabel("Powered By:")); - String procyon = "https://bitbucket.org/mstrobel/procyon"; - link = new JLabel("" + procyon + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(procyon, link)); - pane.add(link); - pane.add(new JLabel("Version: " + Procyon.version())); - pane.add(new JLabel("(c) 2018 Mike Strobel")); - String rsyntax = "https://github.com/bobbylight/RSyntaxTextArea"; - link = new JLabel("" + rsyntax + ""); - link.setCursor(new Cursor(Cursor.HAND_CURSOR)); - link.addMouseListener(new LinkListener(rsyntax, link)); - pane.add(link); - pane.add(new JLabel("Version: 3.0.2")); - pane.add(new JLabel("(c) 2019 Robert Futrell")); - pane.add(new JLabel(" ")); - JOptionPane.showMessageDialog(null, pane); - } - }); - helpMenu.add(menuItem); - } - - private void populateSettingsFromSettingsMenu() { - // synchronized: do not disturb decompiler at work (synchronize every - // time before run decompiler) - synchronized (settings) { - settings.setFlattenSwitchBlocks(flattenSwitchBlocks.isSelected()); - settings.setForceExplicitImports(forceExplicitImports.isSelected()); - settings.setShowSyntheticMembers(showSyntheticMembers.isSelected()); - settings.setExcludeNestedTypes(excludeNestedTypes.isSelected()); - settings.setForceExplicitTypeArguments(forceExplicitTypes.isSelected()); - settings.setRetainRedundantCasts(retainRedundantCasts.isSelected()); - settings.setIncludeErrorDiagnostics(showDebugInfo.isSelected()); - settings.setUnicodeOutputEnabled(unicodeReplacement.isSelected()); - settings.setShowDebugLineNumbers(debugLineNumbers.isSelected()); - // - // Note: You shouldn't ever need to set this. It's only for - // languages that support catch - // blocks without an exception variable. Java doesn't allow this. I - // think Scala does. - // - // settings.setAlwaysGenerateExceptionVariableForCatchBlocks(true); - // - - final ButtonModel selectedLanguage = languagesGroup.getSelection(); - if (selectedLanguage != null) { - final Language language = languageLookup.get(selectedLanguage.getActionCommand()); - - if (language != null) - settings.setLanguage(language); - } - - if (java.isSelected()) { - settings.setLanguage(Languages.java()); - } else if (bytecode.isSelected()) { - settings.setLanguage(Languages.bytecode()); - } else if (bytecodeAST.isSelected()) { - settings.setLanguage(Languages.bytecodeAst()); - } - settings.setIncludeLineNumbersInBytecode(bytecodeLineNumbers.isSelected()); - } - } - - private class ThemeAction extends AbstractAction { - private static final long serialVersionUID = -6618680171943723199L; - private String xml; - - public ThemeAction(String name, String xml) { - putValue(NAME, name); - this.xml = xml; - } - - @Override - public void actionPerformed(ActionEvent e) { - luytenPrefs.setThemeXml(xml); - mainWindow.onThemesChanged(); - } - } - - private class LinkListener extends MouseAdapter { - String link; - JLabel label; - - public LinkListener(String link, JLabel label) { - this.link = link; - this.label = label; - } - - @Override - public void mouseClicked(MouseEvent e) { - try { - Desktop.getDesktop().browse(new URI(link)); - } catch (Exception e1) { - e1.printStackTrace(); - } - } - - @Override - public void mouseEntered(MouseEvent e) { - label.setText("" + link + ""); - } - - @Override - public void mouseExited(MouseEvent e) { - label.setText("" + link + ""); - } - - } -} diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java deleted file mode 100644 index ab89c04f..00000000 --- a/src/us/deathmarine/luyten/MainWindow.java +++ /dev/null @@ -1,498 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Toolkit; -import java.awt.dnd.DropTarget; -import java.awt.event.ActionEvent; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.*; -import java.util.concurrent.Callable; - -import javax.swing.*; -import javax.swing.border.BevelBorder; -import javax.swing.plaf.basic.BasicTabbedPaneUI; - -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; - -/** - * Dispatcher - */ -public class MainWindow extends JFrame { - private static final long serialVersionUID = 5265556630724988013L; - - private static final String TITLE = "Luyten"; - private static final String DEFAULT_TAB = "#DEFAULT"; - - private JProgressBar bar; - private JLabel label; - FindBox findBox; - private FindAllBox findAllBox; - private ConfigSaver configSaver; - private WindowPosition windowPosition; - private LuytenPreferences luytenPrefs; - private FileDialog fileDialog; - private FileSaver fileSaver; - private JTabbedPane jarsTabbedPane; - private Map jarModels; - public MainMenuBar mainMenuBar; - - public MainWindow(File fileFromCommandLine) { - configSaver = ConfigSaver.getLoadedInstance(); - windowPosition = configSaver.getMainWindowPosition(); - luytenPrefs = configSaver.getLuytenPreferences(); - - jarModels = new HashMap(); - mainMenuBar = new MainMenuBar(this); - this.setJMenuBar(mainMenuBar); - - this.adjustWindowPositionBySavedState(); - this.setHideFindBoxOnMainWindowFocus(); - this.setShowFindAllBoxOnMainWindowFocus(); - this.setQuitOnWindowClosing(); - this.setTitle(TITLE); - this.setIconImage(new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/Luyten.png"))).getImage()); - - JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); - label = new JLabel(); - label.setHorizontalAlignment(JLabel.LEFT); - panel1.setBorder(new BevelBorder(BevelBorder.LOWERED)); - panel1.setPreferredSize(new Dimension(this.getWidth() / 2, 20)); - panel1.add(label); - - JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - bar = new JProgressBar(); - - bar.setStringPainted(true); - bar.setOpaque(false); - bar.setVisible(false); - panel2.setPreferredSize(new Dimension(this.getWidth() / 3, 20)); - panel2.add(bar); - - jarsTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); - jarsTabbedPane.setUI(new BasicTabbedPaneUI() { - @Override - protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) { - if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) == -1) - return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height); - else - return 0; - } - }); - jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); - this.getContentPane().add(jarsTabbedPane); - - JSplitPane spt = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, panel2) { - private static final long serialVersionUID = 2189946972124687305L; - private final int location = 400; - - { - setDividerLocation(location); - } - - @Override - public int getDividerLocation() { - return location; - } - - @Override - public int getLastDividerLocation() { - return location; - } - }; - spt.setBorder(new BevelBorder(BevelBorder.LOWERED)); - spt.setPreferredSize(new Dimension(this.getWidth(), 24)); - this.add(spt, BorderLayout.SOUTH); - Model jarModel = null; - if (fileFromCommandLine != null) { - jarModel = loadNewFile(fileFromCommandLine); - } - - try { - DropTarget dt = new DropTarget(); - dt.addDropTargetListener(new DropListener(this)); - this.setDropTarget(dt); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - - fileDialog = new FileDialog(this); - fileSaver = new FileSaver(bar, label); - - if (jarModel != null) { - this.setExitOnEscWhenEnabled(jarModel); - } - - if (jarModel != null && (fileFromCommandLine.getName().toLowerCase().endsWith(".jar") - || fileFromCommandLine.getName().toLowerCase().endsWith(".zip"))) { - jarModel.startWarmUpThread(); - } - - if(RecentFiles.load() > 0) mainMenuBar.updateRecentFiles(); - } - - private void createDefaultTab() { - jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); - } - - private void removeDefaultTab() { - jarsTabbedPane.remove(jarsTabbedPane.indexOfTab(DEFAULT_TAB)); - } - - public void onOpenFileMenu() { - File selectedFile = fileDialog.doOpenDialog(); - if (selectedFile != null) { - System.out.println("[Open]: Opening " + selectedFile.getAbsolutePath()); - this.loadNewFile(selectedFile); - } - } - - public Model loadNewFile(final File file) { - // In case we open the same file again - // we remove the old entry to force a refresh - if (jarModels.containsKey(file.getAbsolutePath())) { - jarModels.remove(file.getAbsolutePath()); - int index = jarsTabbedPane.indexOfTab(file.getName()); - jarsTabbedPane.remove(index); - } - - Model jarModel = new Model(this); - jarModel.loadFile(file); - jarModels.put(file.getAbsolutePath(), jarModel); - jarsTabbedPane.addTab(file.getName(), jarModel); - jarsTabbedPane.setSelectedComponent(jarModel); - - final String tabName = file.getName(); - int index = jarsTabbedPane.indexOfTab(tabName); - Model.Tab tabUI = new Model.Tab(tabName, new Callable() { - @Override - public Void call() { - int index = jarsTabbedPane.indexOfTab(tabName); - jarModels.remove(file.getAbsolutePath()); - jarsTabbedPane.remove(index); - if (jarsTabbedPane.getTabCount() == 0) { - createDefaultTab(); - } - return null; - } - }); - jarsTabbedPane.setTabComponentAt(index, tabUI); - if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { - removeDefaultTab(); - } - return jarModel; - } - - public void onCloseFileMenu() { - this.getSelectedModel().closeFile(); - jarModels.remove(getSelectedModel()); - } - - public void onSaveAsMenu() { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane == null) - return; - String tabTitle = this.getSelectedModel().getCurrentTabTitle(); - if (tabTitle == null) - return; - - String recommendedFileName = tabTitle.replace(".class", ".java"); - File selectedFile = fileDialog.doSaveDialog(recommendedFileName); - if (selectedFile != null) { - fileSaver.saveText(pane.getText(), selectedFile); - } - } - - public void onSaveAllMenu() { - File openedFile = this.getSelectedModel().getOpenedFile(); - if (openedFile == null) - return; - - String fileName = openedFile.getName(); - if (fileName.endsWith(".class")) { - fileName = fileName.replace(".class", ".java"); - } else if (fileName.toLowerCase().endsWith(".jar")) { - fileName = "decompiled-" + fileName.replaceAll("\\.[jJ][aA][rR]", ".zip"); - } else { - fileName = "saved-" + fileName; - } - - File selectedFileToSave = fileDialog.doSaveAllDialog(fileName); - if (selectedFileToSave != null) { - fileSaver.saveAllDecompiled(openedFile, selectedFileToSave); - } - } - - public void onExitMenu() { - quit(); - } - - public void onSelectAllMenu() { - try { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - pane.requestFocusInWindow(); - pane.setSelectionStart(0); - pane.setSelectionEnd(pane.getText().length()); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onFindMenu() { - try { - RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); - if (pane != null) { - if (findBox == null) - findBox = new FindBox(this); - findBox.showFindBox(); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onFindAllMenu() { - try { - if (findAllBox == null) - findAllBox = new FindAllBox(this); - findAllBox.showFindBox(); - - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onLegalMenu() { - new Thread() { - public void run() { - try { - bar.setVisible(true); - bar.setIndeterminate(true); - String legalStr = getLegalStr(); - getSelectedModel().showLegal(legalStr); - } finally { - bar.setIndeterminate(false); - bar.setVisible(false); - } - } - }.start(); - } - - public void onListLoadedClasses() { - try { - StringBuilder sb = new StringBuilder(); - ClassLoader myCL = Thread.currentThread().getContextClassLoader(); - bar.setVisible(true); - bar.setIndeterminate(true); - while (myCL != null) { - sb.append("ClassLoader: " + myCL + "\n"); - for (Iterator iter = list(myCL); iter.hasNext();) { - sb.append("\t" + iter.next() + "\n"); - } - myCL = myCL.getParent(); - } - this.getSelectedModel().show("Debug", sb.toString()); - } finally { - bar.setIndeterminate(false); - bar.setVisible(false); - } - } - - private static Iterator list(ClassLoader CL) { - Class CL_class = CL.getClass(); - while (CL_class != java.lang.ClassLoader.class) { - CL_class = CL_class.getSuperclass(); - } - java.lang.reflect.Field ClassLoader_classes_field; - try { - ClassLoader_classes_field = CL_class.getDeclaredField("classes"); - ClassLoader_classes_field.setAccessible(true); - Vector classes = (Vector) ClassLoader_classes_field.get(CL); - return classes.iterator(); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - Luyten.showExceptionDialog("Exception!", e); - } - return null; - } - - private String getLegalStr() { - StringBuilder sb = new StringBuilder(); - try { - BufferedReader reader = new BufferedReader( - new InputStreamReader(getClass().getResourceAsStream("/distfiles/Procyon.License.txt"))); - String line; - while ((line = reader.readLine()) != null) - sb.append(line).append("\n"); - sb.append("\n\n\n\n\n"); - reader = new BufferedReader( - new InputStreamReader(getClass().getResourceAsStream("/distfiles/RSyntaxTextArea.License.txt"))); - while ((line = reader.readLine()) != null) - sb.append(line).append("\n"); - } catch (IOException e) { - Luyten.showExceptionDialog("Exception!", e); - } - return sb.toString(); - } - - public void onThemesChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.changeTheme(luytenPrefs.getThemeXml()); - luytenPrefs.setFont_size(jarModel.getTheme().baseFont.getSize()); - } - } - - public void onSettingsChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.updateOpenClasses(); - } - } - - public void onTreeSettingsChanged() { - for (Model jarModel : jarModels.values()) { - jarModel.updateTree(); - } - } - - public void onFileDropped(File file) { - if (file != null) { - this.loadNewFile(file); - } - } - - public void onFileLoadEnded(File file, boolean isSuccess) { - try { - if (file != null && isSuccess) { - this.setTitle(TITLE + " - " + file.getName()); - } else { - this.setTitle(TITLE); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - public void onNavigationRequest(String uniqueStr) { - this.getSelectedModel().navigateTo(uniqueStr); - } - - private void adjustWindowPositionBySavedState() { - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - if (!windowPosition.isSavedWindowPositionValid()) { - final Dimension center = new Dimension((int) (screenSize.width * 0.75), (int) (screenSize.height * 0.75)); - final int x = (int) (center.width * 0.2); - final int y = (int) (center.height * 0.2); - this.setBounds(x, y, center.width, center.height); - - } else if (windowPosition.isFullScreen()) { - int heightMinusTray = screenSize.height; - if (screenSize.height > 30) - heightMinusTray -= 30; - this.setBounds(0, 0, screenSize.width, heightMinusTray); - this.setExtendedState(JFrame.MAXIMIZED_BOTH); - - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - if (MainWindow.this.getExtendedState() != JFrame.MAXIMIZED_BOTH) { - windowPosition.setFullScreen(false); - if (windowPosition.isSavedWindowPositionValid()) { - MainWindow.this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), - windowPosition.getWindowWidth(), windowPosition.getWindowHeight()); - } - MainWindow.this.removeComponentListener(this); - } - } - }); - - } else { - this.setBounds(windowPosition.getWindowX(), windowPosition.getWindowY(), windowPosition.getWindowWidth(), - windowPosition.getWindowHeight()); - } - } - - private void setHideFindBoxOnMainWindowFocus() { - this.addWindowFocusListener(new WindowAdapter() { - @Override - public void windowGainedFocus(WindowEvent e) { - if (findBox != null && findBox.isVisible()) { - findBox.setVisible(false); - } - } - }); - } - - private void setShowFindAllBoxOnMainWindowFocus() { - this.addWindowFocusListener(new WindowAdapter() { - @Override - public void windowGainedFocus(WindowEvent e) { - if (findAllBox != null && findAllBox.isVisible()) { - findAllBox.setVisible(false); - } - } - }); - } - - private void setQuitOnWindowClosing() { - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - quit(); - } - }); - } - - private void quit() { - try { - windowPosition.readPositionFromWindow(this); - configSaver.saveConfig(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } finally { - try { - this.dispose(); - } finally { - System.exit(0); - } - } - } - - private void setExitOnEscWhenEnabled(JComponent mainComponent) { - Action escapeAction = new AbstractAction() { - private static final long serialVersionUID = -3460391555954575248L; - - @Override - public void actionPerformed(ActionEvent e) { - if (luytenPrefs.isExitByEscEnabled()) { - quit(); - } - } - }; - KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); - mainComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(escapeKeyStroke, "ESCAPE"); - mainComponent.getActionMap().put("ESCAPE", escapeAction); - } - - public Model getSelectedModel() { - return (Model) jarsTabbedPane.getSelectedComponent(); - } - - public JProgressBar getBar() { - return bar; - } - - public JLabel getLabel() { - return label; - } -} diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java deleted file mode 100644 index 2ecfb8e2..00000000 --- a/src/us/deathmarine/luyten/Model.java +++ /dev/null @@ -1,1062 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.event.*; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.concurrent.Callable; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTree; -import javax.swing.KeyStroke; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.TreeExpansionEvent; -import javax.swing.event.TreeExpansionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.Theme; -import org.fife.ui.rtextarea.RTextScrollPane; -import com.strobel.assembler.InputTypeLoader; -import com.strobel.assembler.metadata.ITypeLoader; -import com.strobel.assembler.metadata.JarTypeLoader; -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.assembler.metadata.TypeReference; -import com.strobel.core.StringUtilities; -import com.strobel.core.VerifyArgument; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; - -/** - * Jar-level model - */ -public class Model extends JSplitPane { - private static final long serialVersionUID = 6896857630400910200L; - - private static final long MAX_JAR_FILE_SIZE_BYTES = 10_000_000_000L; - private static final long MAX_UNPACKED_FILE_SIZE_BYTES = 10_000_000L; - - private static LuytenTypeLoader typeLoader = new LuytenTypeLoader(); - public static MetadataSystem metadataSystem = new MetadataSystem(typeLoader); - - private JTree tree; - public JTabbedPane house; - private File file; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; - private Theme theme; - private MainWindow mainWindow; - private JProgressBar bar; - private JLabel label; - private HashSet hmap = new HashSet(); - private Set treeExpansionState; - private boolean open = false; - private State state; - private ConfigSaver configSaver; - private LuytenPreferences luytenPrefs; - - public Model(MainWindow mainWindow) { - this.mainWindow = mainWindow; - this.bar = mainWindow.getBar(); - this.setLabel(mainWindow.getLabel()); - - configSaver = ConfigSaver.getLoadedInstance(); - settings = configSaver.getDecompilerSettings(); - luytenPrefs = configSaver.getLuytenPreferences(); - - try { - String themeXml = luytenPrefs.getThemeXml(); - setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); - } catch (Exception e1) { - try { - Luyten.showExceptionDialog("Exception!", e1); - String themeXml = LuytenPreferences.DEFAULT_THEME_XML; - luytenPrefs.setThemeXml(themeXml); - setTheme(Theme.load(getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + themeXml))); - } catch (Exception e2) { - Luyten.showExceptionDialog("Exception!", e2); - } - } - - tree = new JTree(); - tree.setModel(new DefaultTreeModel(null)); - tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - tree.setCellRenderer(new CellRenderer()); - TreeListener tl = new TreeListener(); - tree.addMouseListener(tl); - tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener()); - tree.addKeyListener(new KeyAdapter() { - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - openEntryByTreePath(tree.getSelectionPath()); - } - } - }); - - JPanel panel2 = new JPanel(); - panel2.setLayout(new BoxLayout(panel2, 1)); - panel2.setBorder(BorderFactory.createTitledBorder("Structure")); - panel2.add(new JScrollPane(tree)); - - house = new JTabbedPane(); - house.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - house.addChangeListener(new TabChangeListener()); - house.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isMiddleMouseButton(e)) { - closeOpenTab(house.getSelectedIndex()); - } - } - }); - - KeyStroke sfuncF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, Keymap.ctrlDownModifier(), false); - mainWindow.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(sfuncF4, "CloseTab"); - - mainWindow.getRootPane().getActionMap().put("CloseTab", new AbstractAction() { - private static final long serialVersionUID = -885398399200419492L; - - @Override - public void actionPerformed(ActionEvent e) { - closeOpenTab(house.getSelectedIndex()); - } - - }); - - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, 1)); - panel.setBorder(BorderFactory.createTitledBorder("Code")); - panel.add(house); - this.setOrientation(JSplitPane.HORIZONTAL_SPLIT); - this.setDividerLocation(250 % mainWindow.getWidth()); - this.setLeftComponent(panel2); - this.setRightComponent(panel); - - decompilationOptions = new DecompilationOptions(); - decompilationOptions.setSettings(settings); - decompilationOptions.setFullDecompilation(true); - } - - public void showLegal(String legalStr) { - show("Legal", legalStr); - } - - public void show(String name, String contents) { - OpenFile open = new OpenFile(name, "*/" + name, getTheme(), mainWindow); - open.setContent(contents); - hmap.add(open); - addOrSwitchToTab(open); - } - - private void addOrSwitchToTab(final OpenFile open) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - final String title = open.name; - RTextScrollPane rTextScrollPane = open.scrollPane; - int index = house.indexOfTab(title); - if (index > -1 && house.getTabComponentAt(index) != open.scrollPane) { - index = -1; - for (int i = 0; i < house.getTabCount(); i++) { - if (house.getComponentAt(i) == open.scrollPane) { - index = i; - break; - } - } - } - if (index < 0) { - house.addTab(title, rTextScrollPane); - index = house.indexOfComponent(rTextScrollPane); - house.setSelectedIndex(index); - Tab ct = new Tab(title, new Callable() { - @Override - public Void call() throws Exception { - int index = house.indexOfTab(title); - closeOpenTab(index); - return null; - } - }); - house.setTabComponentAt(index, ct); - } else { - house.setSelectedIndex(index); - } - open.onAddedToScreen(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }); - } - - public void closeOpenTab(int index) { - RTextScrollPane co = (RTextScrollPane) house.getComponentAt(index); - RSyntaxTextArea pane = (RSyntaxTextArea) co.getViewport().getView(); - OpenFile open = null; - for (OpenFile file : hmap) - if (pane.equals(file.textArea)) - open = file; - if (open != null && hmap.contains(open)) - hmap.remove(open); - house.remove(co); - if (open != null) - open.close(); - } - - private String getName(String path) { - if (path == null) - return ""; - int i = path.lastIndexOf("/"); - if (i == -1) - i = path.lastIndexOf("\\"); - if (i != -1) - return path.substring(i + 1); - return path; - } - - private class TreeListener extends MouseAdapter { - @Override - public void mousePressed(MouseEvent event) { - boolean isClickCountMatches = (event.getClickCount() == 1 && luytenPrefs.isSingleClickOpenEnabled()) - || (event.getClickCount() == 2 && !luytenPrefs.isSingleClickOpenEnabled()); - if (!isClickCountMatches) - return; - - if (!SwingUtilities.isLeftMouseButton(event)) - return; - - final TreePath trp = tree.getPathForLocation(event.getX(), event.getY()); - if (trp == null) - return; - - Object lastPathComponent = trp.getLastPathComponent(); - boolean isLeaf = (lastPathComponent instanceof TreeNode && ((TreeNode) lastPathComponent).isLeaf()); - if (!isLeaf) - return; - - new Thread() { - public void run() { - openEntryByTreePath(trp); - } - }.start(); - } - } - - private class FurtherExpandingTreeExpansionListener implements TreeExpansionListener { - @Override - public void treeExpanded(final TreeExpansionEvent event) { - final TreePath treePath = event.getPath(); - - final Object expandedTreePathObject = treePath.getLastPathComponent(); - if (!(expandedTreePathObject instanceof TreeNode)) { - return; - } - - final TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject; - if (expandedTreeNode.getChildCount() == 1) { - final TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0); - - if (descendantTreeNode.isLeaf()) { - return; - } - - final TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode); - tree.expandPath(nextTreePath); - } - } - - @Override - public void treeCollapsed(final TreeExpansionEvent event) { - - } - } - - public void openEntryByTreePath(TreePath trp) { - String name = ""; - String path = ""; - try { - bar.setVisible(true); - if (trp.getPathCount() > 1) { - for (int i = 1; i < trp.getPathCount(); i++) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); - TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); - if (i == trp.getPathCount() - 1) { - name = userObject.getOriginalName(); - } else { - path = path + userObject.getOriginalName() + "/"; - } - } - path = path + name; - - if (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) { - if (state == null) { - JarFile jfile = new JarFile(file); - ITypeLoader jarLoader = new JarTypeLoader(jfile); - - typeLoader.getTypeLoaders().add(jarLoader); - state = new State(file.getCanonicalPath(), file, jfile, jarLoader); - } - - JarEntry entry = state.jarFile.getJarEntry(path); - if (entry == null) { - throw new FileEntryNotFoundException(); - } - if (entry.getSize() > MAX_UNPACKED_FILE_SIZE_BYTES) { - throw new TooLargeFileException(entry.getSize()); - } - String entryName = entry.getName(); - if (entryName.endsWith(".class")) { - getLabel().setText("Extracting: " + name); - String internalName = StringUtilities.removeRight(entryName, ".class"); - TypeReference type = metadataSystem.lookupType(internalName); - extractClassToTextPane(type, name, path, null); - } else { - getLabel().setText("Opening: " + name); - try (InputStream in = state.jarFile.getInputStream(entry);) { - extractSimpleFileEntryToTextPane(in, name, path); - } - } - } - } else { - name = file.getName(); - path = file.getPath().replaceAll("\\\\", "/"); - if (file.length() > MAX_UNPACKED_FILE_SIZE_BYTES) { - throw new TooLargeFileException(file.length()); - } - if (name.endsWith(".class")) { - getLabel().setText("Extracting: " + name); - TypeReference type = metadataSystem.lookupType(path); - extractClassToTextPane(type, name, path, null); - } else { - getLabel().setText("Opening: " + name); - try (InputStream in = new FileInputStream(file);) { - extractSimpleFileEntryToTextPane(in, name, path); - } - } - } - - getLabel().setText("Complete"); - } catch (FileEntryNotFoundException e) { - getLabel().setText("File not found: " + name); - } catch (FileIsBinaryException e) { - getLabel().setText("Binary resource: " + name); - } catch (TooLargeFileException e) { - getLabel().setText("File is too large: " + name + " - size: " + e.getReadableFileSize()); - } catch (Exception e) { - getLabel().setText("Cannot open: " + name); - Luyten.showExceptionDialog("Unable to open file!", e); - } finally { - bar.setVisible(false); - } - } - - void extractClassToTextPane(TypeReference type, String tabTitle, String path, String navigatonLink) - throws Exception { - if (tabTitle == null || tabTitle.trim().length() < 1 || path == null) { - throw new FileEntryNotFoundException(); - } - OpenFile sameTitledOpen = null; - for (OpenFile nextOpen : hmap) { - if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path) && type.equals(nextOpen.getType())) { - sameTitledOpen = nextOpen; - break; - } - } - if (sameTitledOpen != null && sameTitledOpen.isContentValid()) { - sameTitledOpen.setInitialNavigationLink(navigatonLink); - addOrSwitchToTab(sameTitledOpen); - return; - } - - // resolve TypeDefinition - TypeDefinition resolvedType = null; - if (type == null || ((resolvedType = type.resolve()) == null)) { - throw new Exception("Unable to resolve type."); - } - - // open tab, store type information, start decompilation - if (sameTitledOpen != null) { - sameTitledOpen.path = path; - sameTitledOpen.invalidateContent(); - sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - sameTitledOpen.setType(resolvedType); - sameTitledOpen.setInitialNavigationLink(navigatonLink); - sameTitledOpen.resetScrollPosition(); - sameTitledOpen.decompile(); - addOrSwitchToTab(sameTitledOpen); - } else { - OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); - open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - open.setType(resolvedType); - open.setInitialNavigationLink(navigatonLink); - open.decompile(); - hmap.add(open); - addOrSwitchToTab(open); - } - } - - public void extractSimpleFileEntryToTextPane(InputStream inputStream, String tabTitle, String path) - throws Exception { - if (inputStream == null || tabTitle == null || tabTitle.trim().length() < 1 || path == null) { - throw new FileEntryNotFoundException(); - } - OpenFile sameTitledOpen = null; - for (OpenFile nextOpen : hmap) { - if (tabTitle.equals(nextOpen.name) && path.equals(nextOpen.path)) { - sameTitledOpen = nextOpen; - break; - } - } - if (sameTitledOpen != null) { - addOrSwitchToTab(sameTitledOpen); - return; - } - - // build tab content - StringBuilder sb = new StringBuilder(); - long nonprintableCharactersCount = 0; - try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader reader = new BufferedReader(inputStreamReader);) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - - for (byte nextByte : line.getBytes()) { - if (nextByte <= 0) { - nonprintableCharactersCount++; - } - } - - } - } - - // guess binary or text - String extension = "." + tabTitle.replaceAll("^[^\\.]*$", "").replaceAll("[^\\.]*\\.", ""); - boolean isTextFile = (OpenFile.WELL_KNOWN_TEXT_FILE_EXTENSIONS.contains(extension) - || nonprintableCharactersCount < sb.length() / 5); - if (!isTextFile) { - throw new FileIsBinaryException(); - } - - // open tab - if (sameTitledOpen != null) { - sameTitledOpen.path = path; - sameTitledOpen.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - sameTitledOpen.resetScrollPosition(); - sameTitledOpen.setContent(sb.toString()); - addOrSwitchToTab(sameTitledOpen); - } else { - OpenFile open = new OpenFile(tabTitle, path, getTheme(), mainWindow); - open.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - open.setContent(sb.toString()); - hmap.add(open); - addOrSwitchToTab(open); - } - } - - private class TabChangeListener implements ChangeListener { - @Override - public void stateChanged(ChangeEvent e) { - int selectedIndex = house.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - for (OpenFile open : hmap) { - if (house.indexOfTab(open.name) == selectedIndex) { - - if (open.getType() != null && !open.isContentValid()) { - updateOpenClass(open); - break; - } - - } - } - } - } - - public void updateOpenClasses() { - // invalidate all open classes (update will hapen at tab change) - for (OpenFile open : hmap) { - if (open.getType() != null) { - open.invalidateContent(); - } - } - // update the current open tab - if it is a class - for (OpenFile open : hmap) { - if (open.getType() != null && isTabInForeground(open)) { - updateOpenClass(open); - break; - } - } - } - - private void updateOpenClass(final OpenFile open) { - if (open.getType() == null) { - return; - } - new Thread(new Runnable() { - @Override - public void run() { - try { - bar.setVisible(true); - getLabel().setText("Extracting: " + open.name); - open.invalidateContent(); - open.decompile(); - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Error, cannot update: " + open.name); - } finally { - bar.setVisible(false); - } - } - }).start(); - } - - private boolean isTabInForeground(OpenFile open) { - String title = open.name; - int selectedIndex = house.getSelectedIndex(); - return (selectedIndex >= 0 && selectedIndex == house.indexOfTab(title)); - } - - final class State implements AutoCloseable { - private final String key; - private final File file; - final JarFile jarFile; - final ITypeLoader typeLoader; - - private State(String key, File file, JarFile jarFile, ITypeLoader typeLoader) { - this.key = VerifyArgument.notNull(key, "key"); - this.file = VerifyArgument.notNull(file, "file"); - this.jarFile = jarFile; - this.typeLoader = typeLoader; - } - - @Override - public void close() { - if (typeLoader != null) { - Model.typeLoader.getTypeLoaders().remove(typeLoader); - } - Closer.tryClose(jarFile); - } - - public File getFile() { - return file; - } - - public String getKey() { - return key; - } - } - - public static class Tab extends JPanel { - private JLabel tabTitle; - private JLabel closeButton = new JLabel(new ImageIcon( - Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/icon_close.png")))); - - public Tab(String title, final Callable onCloseTabAction) { - super(new GridBagLayout()); - this.setOpaque(false); - this.tabTitle = new JLabel(title); - this.createTab(); - closeButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - try { - if (onCloseTabAction != null) { - onCloseTabAction.call(); - } - } catch (Exception ex) { - Luyten.showExceptionDialog("Exception!", ex); - } - } - }); - } - - public void createTab() { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.weightx = 1; - this.add(tabTitle, gbc); - gbc.gridx++; - gbc.insets = new Insets(0, 5, 0, 0); - gbc.anchor = GridBagConstraints.EAST; - this.add(closeButton, gbc); - } - } - - private class CloseTab extends MouseAdapter { - String title; - - public CloseTab(String title) { - this.title = title; - } - - @Override - public void mouseClicked(MouseEvent e) { - int index = house.indexOfTab(title); - closeOpenTab(index); - } - } - - public DefaultMutableTreeNode loadNodesByNames(DefaultMutableTreeNode node, List originalNames) { - List args = new ArrayList<>(); - for (String originalName : originalNames) { - args.add(new TreeNodeUserObject(originalName)); - } - return loadNodesByUserObj(node, args); - } - - public DefaultMutableTreeNode loadNodesByUserObj(DefaultMutableTreeNode node, List args) { - if (args.size() > 0) { - TreeNodeUserObject name = args.remove(0); - DefaultMutableTreeNode nod = getChild(node, name); - if (nod == null) - nod = new DefaultMutableTreeNode(name); - node.add(loadNodesByUserObj(nod, args)); - } - return node; - } - - @SuppressWarnings("unchecked") - public DefaultMutableTreeNode getChild(DefaultMutableTreeNode node, TreeNodeUserObject name) { - Enumeration entry = node.children(); - while (entry.hasMoreElements()) { - DefaultMutableTreeNode nods = entry.nextElement(); - if (((TreeNodeUserObject) nods.getUserObject()).getOriginalName().equals(name.getOriginalName())) { - return nods; - } - } - return null; - } - - public void loadFile(File file) { - if (open) - closeFile(); - this.file = file; - - RecentFiles.add(file.getAbsolutePath()); - mainWindow.mainMenuBar.updateRecentFiles(); - loadTree(); - } - - public void updateTree() { - TreeUtil treeUtil = new TreeUtil(tree); - treeExpansionState = treeUtil.getExpansionState(); - loadTree(); - } - - public void loadTree() { - new Thread(new Runnable() { - @Override - public void run() { - try { - if (file == null) { - return; - } - tree.setModel(new DefaultTreeModel(null)); - - if (file.length() > MAX_JAR_FILE_SIZE_BYTES) { - throw new TooLargeFileException(file.length()); - } - if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) { - JarFile jfile; - jfile = new JarFile(file); - getLabel().setText("Loading: " + jfile.getName()); - bar.setVisible(true); - - JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); - List mass = null; - if (luytenPrefs.isFilterOutInnerClassEntries()) { - mass = jarEntryFilter.getEntriesWithoutInnerClasses(); - } else { - mass = jarEntryFilter.getAllEntriesFromJar(); - } - buildTreeFromMass(mass); - - if (state == null) { - ITypeLoader jarLoader = new JarTypeLoader(jfile); - typeLoader.getTypeLoaders().add(jarLoader); - state = new State(file.getCanonicalPath(), file, jfile, jarLoader); - } - open = true; - getLabel().setText("Complete"); - } else { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - final DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - tree.setModel(new DefaultTreeModel(top)); - settings.setTypeLoader(new InputTypeLoader()); - open = true; - getLabel().setText("Complete"); - - // open it automatically - new Thread() { - public void run() { - TreePath trp = new TreePath(top.getPath()); - openEntryByTreePath(trp); - }; - }.start(); - } - - if (treeExpansionState != null) { - try { - TreeUtil treeUtil = new TreeUtil(tree); - treeUtil.restoreExpanstionState(treeExpansionState); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - } catch (TooLargeFileException e) { - getLabel().setText("File is too large: " + file.getName() + " - size: " + e.getReadableFileSize()); - closeFile(); - } catch (Exception e1) { - Luyten.showExceptionDialog("Cannot open " + file.getName() + "!", e1); - getLabel().setText("Cannot open: " + file.getName()); - closeFile(); - } finally { - mainWindow.onFileLoadEnded(file, open); - bar.setVisible(false); - } - } - - }).start(); - } - - private void buildTreeFromMass(List mass) { - if (luytenPrefs.isPackageExplorerStyle()) { - buildFlatTreeFromMass(mass); - } else { - buildDirectoryTreeFromMass(mass); - } - } - - private void buildDirectoryTreeFromMass(List mass) { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - List sort = new ArrayList(); - Collections.sort(mass, String.CASE_INSENSITIVE_ORDER); - for (String m : mass) - if (m.contains("META-INF") && !sort.contains(m)) - sort.add(m); - Set set = new HashSet(); - for (String m : mass) { - if (m.contains("/")) { - set.add(m.substring(0, m.lastIndexOf("/") + 1)); - } - } - List packs = Arrays.asList(set.toArray(new String[] {})); - Collections.sort(packs, String.CASE_INSENSITIVE_ORDER); - Collections.sort(packs, new Comparator() { - public int compare(String o1, String o2) { - return o2.split("/").length - o1.split("/").length; - } - }); - for (String pack : packs) - for (String m : mass) - if (!m.contains("META-INF") && m.contains(pack) && !m.replace(pack, "").contains("/")) - sort.add(m); - for (String m : mass) - if (!m.contains("META-INF") && !m.contains("/") && !sort.contains(m)) - sort.add(m); - for (String pack : sort) { - LinkedList list = new LinkedList(Arrays.asList(pack.split("/"))); - loadNodesByNames(top, list); - } - tree.setModel(new DefaultTreeModel(top)); - } - - private void buildFlatTreeFromMass(List mass) { - TreeNodeUserObject topNodeUserObject = new TreeNodeUserObject(getName(file.getName())); - DefaultMutableTreeNode top = new DefaultMutableTreeNode(topNodeUserObject); - - TreeMap> packages = new TreeMap<>(); - HashSet classContainingPackageRoots = new HashSet<>(); - - Comparator sortByFileExtensionsComparator = new Comparator() { - // (assertion: mass does not contain null elements) - @Override - public int compare(String o1, String o2) { - int comp = o1.replaceAll("[^\\.]*\\.", "").compareTo(o2.replaceAll("[^\\.]*\\.", "")); - if (comp != 0) - return comp; - return o1.compareTo(o2); - } - }; - - for (String entry : mass) { - String packagePath = ""; - String packageRoot = ""; - if (entry.contains("/")) { - packagePath = entry.replaceAll("/[^/]*$", ""); - packageRoot = entry.replaceAll("/.*$", ""); - } - String packageEntry = entry.replace(packagePath + "/", ""); - if (!packages.containsKey(packagePath)) { - packages.put(packagePath, new TreeSet(sortByFileExtensionsComparator)); - } - packages.get(packagePath).add(packageEntry); - if (!entry.startsWith("META-INF") && packageRoot.trim().length() > 0 - && entry.matches(".*\\.(class|java|prop|properties)$")) { - classContainingPackageRoots.add(packageRoot); - } - } - - // META-INF comes first -> not flat - for (String packagePath : packages.keySet()) { - if (packagePath.startsWith("META-INF")) { - List packagePathElements = Arrays.asList(packagePath.split("/")); - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(packagePathElements); - list.add(entry); - loadNodesByNames(top, list); - } - } - } - - // real packages: path starts with a classContainingPackageRoot -> flat - for (String packagePath : packages.keySet()) { - String packageRoot = packagePath.replaceAll("/.*$", ""); - if (classContainingPackageRoots.contains(packageRoot)) { - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(); - list.add(new TreeNodeUserObject(packagePath, packagePath.replaceAll("/", "."))); - list.add(new TreeNodeUserObject(entry)); - loadNodesByUserObj(top, list); - } - } - } - - // the rest, not real packages but directories -> not flat - for (String packagePath : packages.keySet()) { - String packageRoot = packagePath.replaceAll("/.*$", ""); - if (!classContainingPackageRoots.contains(packageRoot) && !packagePath.startsWith("META-INF") - && packagePath.length() > 0) { - List packagePathElements = Arrays.asList(packagePath.split("/")); - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(packagePathElements); - list.add(entry); - loadNodesByNames(top, list); - } - } - } - - // the default package -> not flat - String packagePath = ""; - if (packages.containsKey(packagePath)) { - for (String entry : packages.get(packagePath)) { - ArrayList list = new ArrayList<>(); - list.add(entry); - loadNodesByNames(top, list); - } - } - tree.setModel(new DefaultTreeModel(top)); - } - - public void closeFile() { - for (OpenFile co : hmap) { - int pos = house.indexOfTab(co.name); - if (pos >= 0) - house.remove(pos); - co.close(); - } - - final State oldState = state; - Model.this.state = null; - if (oldState != null) { - Closer.tryClose(oldState); - } - - hmap.clear(); - tree.setModel(new DefaultTreeModel(null)); - metadataSystem = new MetadataSystem(typeLoader); - file = null; - treeExpansionState = null; - open = false; - mainWindow.onFileLoadEnded(file, open); - } - - public void changeTheme(String xml) { - InputStream in = getClass().getResourceAsStream(LuytenPreferences.THEME_XML_PATH + xml); - try { - if (in != null) { - setTheme(Theme.load(in)); - for (OpenFile f : hmap) { - getTheme().apply(f.textArea); - } - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - } - - public File getOpenedFile() { - File openedFile = null; - if (file != null && open) { - openedFile = file; - } - if (openedFile == null) { - getLabel().setText("No open file"); - } - return openedFile; - } - - public String getCurrentTabTitle() { - String tabTitle = null; - try { - int pos = house.getSelectedIndex(); - if (pos >= 0) { - tabTitle = house.getTitleAt(pos); - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - if (tabTitle == null) { - getLabel().setText("No open tab"); - } - return tabTitle; - } - - public RSyntaxTextArea getCurrentTextArea() { - RSyntaxTextArea currentTextArea = null; - try { - int pos = house.getSelectedIndex(); - System.out.println(pos); - if (pos >= 0) { - RTextScrollPane co = (RTextScrollPane) house.getComponentAt(pos); - currentTextArea = (RSyntaxTextArea) co.getViewport().getView(); - } - } catch (Exception e1) { - Luyten.showExceptionDialog("Exception!", e1); - } - if (currentTextArea == null) { - getLabel().setText("No open tab"); - } - return currentTextArea; - } - - public void startWarmUpThread() { - new Thread() { - public void run() { - try { - Thread.sleep(500); - String internalName = FindBox.class.getName(); - TypeReference type = metadataSystem.lookupType(internalName); - TypeDefinition resolvedType = null; - if ((type == null) || ((resolvedType = type.resolve()) == null)) { - return; - } - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput - .setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); - String decompiledSource = stringwriter.toString(); - OpenFile open = new OpenFile(internalName, "*/" + internalName, getTheme(), mainWindow); - open.setContent(decompiledSource); - JTabbedPane pane = new JTabbedPane(); - pane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - pane.addTab("title", open.scrollPane); - pane.setSelectedIndex(pane.indexOfTab("title")); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }.start(); - } - - public void navigateTo(final String uniqueStr) { - new Thread(new Runnable() { - @Override - public void run() { - if (uniqueStr == null) - return; - String[] linkParts = uniqueStr.split("\\|"); - if (linkParts.length <= 1) - return; - String destinationTypeStr = linkParts[1]; - try { - bar.setVisible(true); - getLabel().setText("Navigating: " + destinationTypeStr.replaceAll("/", ".")); - - TypeReference type = metadataSystem.lookupType(destinationTypeStr); - if (type == null) - throw new RuntimeException("Cannot lookup type: " + destinationTypeStr); - TypeDefinition typeDef = type.resolve(); - if (typeDef == null) - throw new RuntimeException("Cannot resolve type: " + destinationTypeStr); - - String tabTitle = typeDef.getName() + ".class"; - extractClassToTextPane(typeDef, tabTitle, destinationTypeStr, uniqueStr); - - getLabel().setText("Complete"); - } catch (Exception e) { - getLabel().setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); - Luyten.showExceptionDialog("Cannot Navigate!", e); - } finally { - bar.setVisible(false); - } - } - }).start(); - } - - public JLabel getLabel() { - return label; - } - - public void setLabel(JLabel label) { - this.label = label; - } - - public State getState() { - return state; - } - - public Theme getTheme() { - return theme; - } - - public void setTheme(Theme theme) { - this.theme = theme; - } - -} diff --git a/src/us/deathmarine/luyten/OpenFile.java b/src/us/deathmarine/luyten/OpenFile.java deleted file mode 100644 index 497db079..00000000 --- a/src/us/deathmarine/luyten/OpenFile.java +++ /dev/null @@ -1,844 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Font; -import java.awt.Panel; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import java.io.StringWriter; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; - -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JScrollBar; -import javax.swing.JViewport; -import javax.swing.ScrollPaneConstants; -import javax.swing.Scrollable; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.event.HyperlinkEvent; -import org.fife.ui.rsyntaxtextarea.LinkGenerator; -import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.SyntaxConstants; -import org.fife.ui.rsyntaxtextarea.Theme; -import org.fife.ui.rtextarea.RTextScrollPane; - -import com.strobel.assembler.metadata.MetadataSystem; -import com.strobel.assembler.metadata.TypeDefinition; -import com.strobel.decompiler.DecompilationOptions; -import com.strobel.decompiler.DecompilerSettings; -import com.strobel.decompiler.PlainTextOutput; -import com.strobel.decompiler.languages.Languages; - -public class OpenFile implements SyntaxConstants { - - public static final HashSet WELL_KNOWN_TEXT_FILE_EXTENSIONS = new HashSet<>( - Arrays.asList(".java", ".xml", ".rss", ".project", ".classpath", ".h", ".c", ".cpp", ".yaml", ".yml", ".ini", ".sql", ".js", ".php", ".php5", - ".phtml", ".html", ".htm", ".xhtm", ".xhtml", ".lua", ".bat", ".pl", ".sh", ".css", ".json", ".txt", - ".rb", ".make", ".mak", ".py", ".properties", ".prop")); - - // navigation links - private TreeMap selectionToUniqueStrTreeMap = new TreeMap<>(); - private Map isNavigableCache = new ConcurrentHashMap<>(); - private Map readableLinksCache = new ConcurrentHashMap<>(); - - private volatile boolean isContentValid = false; - private volatile boolean isNavigationLinksValid = false; - private volatile boolean isWaitForLinksCursor = false; - private volatile Double lastScrollPercent = null; - - private LinkProvider linkProvider; - private String initialNavigationLink; - private boolean isFirstTimeRun = true; - - MainWindow mainWindow; - RTextScrollPane scrollPane; - Panel image_pane; - RSyntaxTextArea textArea; - String name; - String path; - - private ConfigSaver configSaver; - private LuytenPreferences luytenPrefs; - - // decompiler and type references (not needed for text files) - private MetadataSystem metadataSystem; - private DecompilerSettings settings; - private DecompilationOptions decompilationOptions; - private TypeDefinition type; - - public OpenFile(String name, String path, Theme theme, final MainWindow mainWindow) { - this.name = name; - this.path = path; - this.mainWindow = mainWindow; - - configSaver = ConfigSaver.getLoadedInstance(); - luytenPrefs = configSaver.getLuytenPreferences(); - - textArea = new RSyntaxTextArea(25, 70); - textArea.setCaretPosition(0); - textArea.requestFocusInWindow(); - textArea.setMarkOccurrences(true); - textArea.setClearWhitespaceLinesEnabled(false); - textArea.setEditable(false); - textArea.setAntiAliasingEnabled(true); - textArea.setCodeFoldingEnabled(true); - - if (name.toLowerCase().endsWith(".class") || name.toLowerCase().endsWith(".java")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVA); - else if (name.toLowerCase().endsWith(".xml") || name.toLowerCase().endsWith(".rss") - || name.toLowerCase().endsWith(".project") || name.toLowerCase().endsWith(".classpath")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_XML); - else if (name.toLowerCase().endsWith(".h") || name.toLowerCase().endsWith(".c")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_C); - else if (name.toLowerCase().endsWith(".cpp")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS); - else if (name.toLowerCase().endsWith(".sql")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_SQL); - else if (name.toLowerCase().endsWith(".js")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVASCRIPT); - else if (name.toLowerCase().endsWith(".php") || name.toLowerCase().endsWith(".php5") - || name.toLowerCase().endsWith(".phtml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PHP); - else if (name.toLowerCase().endsWith(".html") || name.toLowerCase().endsWith(".htm") - || name.toLowerCase().endsWith(".xhtm") || name.toLowerCase().endsWith(".xhtml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_HTML); - else if (name.toLowerCase().endsWith(".js")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JAVASCRIPT); - else if (name.toLowerCase().endsWith(".lua")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_LUA); - else if (name.toLowerCase().endsWith(".bat")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_WINDOWS_BATCH); - else if (name.toLowerCase().endsWith(".pl")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PERL); - else if (name.toLowerCase().endsWith(".sh")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_UNIX_SHELL); - else if (name.toLowerCase().endsWith(".css")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_CSS); - else if (name.toLowerCase().endsWith(".json")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_JSON); - else if (name.toLowerCase().endsWith(".ini")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_INI); - else if (name.toLowerCase().endsWith(".yaml") || name.toLowerCase().endsWith(".yml")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_YAML); - else if (name.toLowerCase().endsWith(".rb")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_RUBY); - else if (name.toLowerCase().endsWith(".make") || name.toLowerCase().endsWith(".mak")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_MAKEFILE); - else if (name.toLowerCase().endsWith(".py")) - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_PYTHON); - else - textArea.setSyntaxEditingStyle(SYNTAX_STYLE_NONE); - scrollPane = new RTextScrollPane(textArea, true); - - scrollPane.setIconRowHeaderEnabled(true); - textArea.setText(""); - - // Edit RTextArea's PopupMenu - JPopupMenu pop = textArea.getPopupMenu(); - pop.addSeparator(); - JMenuItem item = new JMenuItem("Font"); - item.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - JFontChooser fontChooser = new JFontChooser(); - fontChooser.setSelectedFont(textArea.getFont()); - fontChooser.setSelectedFontSize(textArea.getFont().getSize()); - int result = fontChooser.showDialog(mainWindow); - if (result == JFontChooser.OK_OPTION) { - textArea.setFont(fontChooser.getSelectedFont()); - luytenPrefs.setFont_size(fontChooser.getSelectedFontSize()); - } - } - }); - pop.add(item); - textArea.setPopupMenu(pop); - - theme.apply(textArea); - - textArea.setFont(new Font(textArea.getFont().getName(), textArea.getFont().getStyle(), luytenPrefs.getFont_size())); - - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - final JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); - if (verticalScrollbar != null) { - verticalScrollbar.addAdjustmentListener(new AdjustmentListener() { - @Override - public void adjustmentValueChanged(AdjustmentEvent e) { - String content = textArea.getText(); - if (content == null || content.length() == 0) - return; - int scrollValue = verticalScrollbar.getValue() - verticalScrollbar.getMinimum(); - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - if (scrollMax < 1 || scrollValue < 0 || scrollValue > scrollMax) - return; - lastScrollPercent = (((double) scrollValue) / ((double) scrollMax)); - } - }); - } - - textArea.setHyperlinksEnabled(true); - textArea.setLinkScanningMask(Keymap.ctrlDownModifier()); - - textArea.setLinkGenerator(new LinkGenerator() { - @Override - public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) { - final String uniqueStr = getUniqueStrForOffset(offs); - final Integer selectionFrom = getSelectionFromForOffset(offs); - if (uniqueStr != null && selectionFrom != null) { - return new LinkGeneratorResult() { - @Override - public HyperlinkEvent execute() { - if (isNavigationLinksValid) - onNavigationClicked(uniqueStr); - return null; - } - - @Override - public int getSourceOffset() { - if (isNavigationLinksValid) - return selectionFrom; - return offs; - } - }; - } - return null; - } - }); - - /* - * Add Ctrl+Wheel Zoom for Text Size Removes all standard listeners and - * writes new listeners for wheelscroll movement. - */ - for (MouseWheelListener listeners : scrollPane.getMouseWheelListeners()) { - scrollPane.removeMouseWheelListener(listeners); - } - ; - scrollPane.addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - if (e.getWheelRotation() == 0) { - // Nothing to do here. This happens when scroll event is delivered from a touchbar - // or MagicMouse. There's getPreciseWheelRotation, however it looks like there's no - // trivial and consistent way to use that - // See https://github.com/JetBrains/intellij-community/blob/21c99af7c78fc82aefc4d05646389f4991b08b38/bin/idea.properties#L133-L156 - return; - } - - if ((e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0) { - Font font = textArea.getFont(); - int size = font.getSize(); - if (e.getWheelRotation() > 0) { - textArea.setFont(new Font(font.getName(), font.getStyle(), --size >= 8 ? --size : 8)); - } else { - textArea.setFont(new Font(font.getName(), font.getStyle(), ++size)); - } - luytenPrefs.setFont_size(size); - } else { - if (scrollPane.isWheelScrollingEnabled() && e.getWheelRotation() != 0) { - JScrollBar toScroll = scrollPane.getVerticalScrollBar(); - int direction = e.getWheelRotation() < 0 ? -1 : 1; - int orientation = SwingConstants.VERTICAL; - if (toScroll == null || !toScroll.isVisible()) { - toScroll = scrollPane.getHorizontalScrollBar(); - if (toScroll == null || !toScroll.isVisible()) { - return; - } - orientation = SwingConstants.HORIZONTAL; - } - e.consume(); - - if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { - JViewport vp = scrollPane.getViewport(); - if (vp == null) { - return; - } - Component comp = vp.getView(); - int units = Math.abs(e.getUnitsToScroll()); - boolean limitScroll = Math.abs(e.getWheelRotation()) == 1; - Object fastWheelScroll = toScroll.getClientProperty("JScrollBar.fastWheelScrolling"); - if (Boolean.TRUE == fastWheelScroll && comp instanceof Scrollable) { - Scrollable scrollComp = (Scrollable) comp; - Rectangle viewRect = vp.getViewRect(); - int startingX = viewRect.x; - boolean leftToRight = comp.getComponentOrientation().isLeftToRight(); - int scrollMin = toScroll.getMinimum(); - int scrollMax = toScroll.getMaximum() - toScroll.getModel().getExtent(); - - if (limitScroll) { - int blockIncr = scrollComp.getScrollableBlockIncrement(viewRect, orientation, - direction); - if (direction < 0) { - scrollMin = Math.max(scrollMin, toScroll.getValue() - blockIncr); - } else { - scrollMax = Math.min(scrollMax, toScroll.getValue() + blockIncr); - } - } - - for (int i = 0; i < units; i++) { - int unitIncr = scrollComp.getScrollableUnitIncrement(viewRect, orientation, - direction); - if (orientation == SwingConstants.VERTICAL) { - if (direction < 0) { - viewRect.y -= unitIncr; - if (viewRect.y <= scrollMin) { - viewRect.y = scrollMin; - break; - } - } else { // (direction > 0 - viewRect.y += unitIncr; - if (viewRect.y >= scrollMax) { - viewRect.y = scrollMax; - break; - } - } - } else { - if ((leftToRight && direction < 0) || (!leftToRight && direction > 0)) { - viewRect.x -= unitIncr; - if (leftToRight) { - if (viewRect.x < scrollMin) { - viewRect.x = scrollMin; - break; - } - } - } else if ((leftToRight && direction > 0) || (!leftToRight && direction < 0)) { - viewRect.x += unitIncr; - if (leftToRight) { - if (viewRect.x > scrollMax) { - viewRect.x = scrollMax; - break; - } - } - } else { - assert false : "Non-sensical ComponentOrientation / scroll direction"; - } - } - } - if (orientation == SwingConstants.VERTICAL) { - toScroll.setValue(viewRect.y); - } else { - if (leftToRight) { - toScroll.setValue(viewRect.x); - } else { - int newPos = toScroll.getValue() - (viewRect.x - startingX); - if (newPos < scrollMin) { - newPos = scrollMin; - } else if (newPos > scrollMax) { - newPos = scrollMax; - } - toScroll.setValue(newPos); - } - } - } else { - int delta; - int limit = -1; - - if (limitScroll) { - if (direction < 0) { - limit = toScroll.getValue() - toScroll.getBlockIncrement(direction); - } else { - limit = toScroll.getValue() + toScroll.getBlockIncrement(direction); - } - } - - for (int i = 0; i < units; i++) { - if (direction > 0) { - delta = toScroll.getUnitIncrement(direction); - } else { - delta = -toScroll.getUnitIncrement(direction); - } - int oldValue = toScroll.getValue(); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - if (oldValue == newValue) { - break; - } - if (limitScroll && i > 0) { - assert limit != -1; - if ((direction < 0 && newValue < limit) - || (direction > 0 && newValue > limit)) { - break; - } - } - toScroll.setValue(newValue); - } - - } - } else if (e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL) { - int oldValue = toScroll.getValue(); - int blockIncrement = toScroll.getBlockIncrement(direction); - int delta = blockIncrement * ((direction > 0) ? +1 : -1); - int newValue = oldValue + delta; - if (delta > 0 && newValue < oldValue) { - newValue = toScroll.getMaximum(); - } else if (delta < 0 && newValue > oldValue) { - newValue = toScroll.getMinimum(); - } - toScroll.setValue(newValue); - } - } - } - - e.consume(); - } - }); - - textArea.addMouseMotionListener(new MouseMotionAdapter() { - private boolean isLinkLabelPrev = false; - private String prevLinkText = null; - - @Override - public synchronized void mouseMoved(MouseEvent e) { - String linkText = null; - boolean isLinkLabel = false; - boolean isCtrlDown = (e.getModifiersEx() & Keymap.ctrlDownModifier()) != 0; - if (isCtrlDown) { - linkText = createLinkLabel(e); - isLinkLabel = linkText != null; - } - if (isCtrlDown && isWaitForLinksCursor) { - textArea.setCursor(new Cursor(Cursor.WAIT_CURSOR)); - } else if (textArea.getCursor().getType() == Cursor.WAIT_CURSOR) { - textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - - JLabel label = OpenFile.this.mainWindow.getLabel(); - - if (isLinkLabel && isLinkLabelPrev) { - if (!linkText.equals(prevLinkText)) { - setLinkLabel(label, linkText); - } - } else if (isLinkLabel && !isLinkLabelPrev) { - setLinkLabel(label, linkText); - - } else if (!isLinkLabel && isLinkLabelPrev) { - setLinkLabel(label, null); - } - isLinkLabelPrev = isLinkLabel; - prevLinkText = linkText; - } - - private void setLinkLabel(JLabel label, String text) { - String current = label.getText(); - if (text == null && current != null) - if (current.startsWith("Navigating:") || current.startsWith("Cannot navigate:")) - return; - label.setText(text != null ? text : "Complete"); - } - - private String createLinkLabel(MouseEvent e) { - int offs = textArea.viewToModel(e.getPoint()); - if (isNavigationLinksValid) { - return getLinkDescriptionForOffset(offs); - } - return null; - } - }); - } - - public void setContent(String content) { - textArea.setText(content); - } - - public void decompile() { - this.invalidateContent(); - // synchronized: do not accept changes from menu while running - synchronized (settings) { - if (Languages.java().getName().equals(settings.getLanguage().getName())) { - decompileWithNavigationLinks(); - } else { - decompileWithoutLinks(); - } - } - } - - private void decompileWithoutLinks() { - this.invalidateContent(); - isNavigationLinksValid = false; - textArea.setHyperlinksEnabled(false); - - StringWriter stringwriter = new StringWriter(); - PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); - plainTextOutput.setUnicodeOutputEnabled(decompilationOptions.getSettings().isUnicodeOutputEnabled()); - settings.getLanguage().decompileType(type, plainTextOutput, decompilationOptions); - setContentPreserveLastScrollPosition(stringwriter.toString()); - this.isContentValid = true; - } - - private void decompileWithNavigationLinks() { - this.invalidateContent(); - DecompilerLinkProvider newLinkProvider = new DecompilerLinkProvider(); - newLinkProvider.setDecompilerReferences(metadataSystem, settings, decompilationOptions); - newLinkProvider.setType(type); - linkProvider = newLinkProvider; - - linkProvider.generateContent(); - setContentPreserveLastScrollPosition(linkProvider.getTextContent()); - this.isContentValid = true; - enableLinks(); - } - - private void setContentPreserveLastScrollPosition(final String content) { - final Double scrollPercent = lastScrollPercent; - if (scrollPercent != null && initialNavigationLink == null) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - textArea.setText(content); - restoreScrollPosition(scrollPercent); - } - }); - } else { - textArea.setText(content); - } - } - - private void restoreScrollPosition(final double position) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - JScrollBar verticalScrollbar = scrollPane.getVerticalScrollBar(); - if (verticalScrollbar == null) - return; - int scrollMax = verticalScrollbar.getMaximum() - verticalScrollbar.getMinimum(); - long newScrollValue = Math.round(position * scrollMax) + verticalScrollbar.getMinimum(); - if (newScrollValue < verticalScrollbar.getMinimum()) - newScrollValue = verticalScrollbar.getMinimum(); - if (newScrollValue > verticalScrollbar.getMaximum()) - newScrollValue = verticalScrollbar.getMaximum(); - verticalScrollbar.setValue((int) newScrollValue); - } - }); - } - - private void enableLinks() { - if (initialNavigationLink != null) { - doEnableLinks(); - } else { - new Thread(new Runnable() { - @Override - public void run() { - try { - isWaitForLinksCursor = true; - doEnableLinks(); - } finally { - isWaitForLinksCursor = false; - resetCursor(); - } - } - }).start(); - } - } - - private void resetCursor() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - textArea.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - } - }); - } - - private void doEnableLinks() { - isNavigationLinksValid = false; - linkProvider.processLinks(); - buildSelectionToUniqueStrTreeMap(); - clearLinksCache(); - isNavigationLinksValid = true; - textArea.setHyperlinksEnabled(true); - warmUpWithFirstLink(); - } - - private void warmUpWithFirstLink() { - if (selectionToUniqueStrTreeMap.keySet().size() > 0) { - Selection selection = selectionToUniqueStrTreeMap.keySet().iterator().next(); - getLinkDescriptionForOffset(selection.from); - } - } - - public void clearLinksCache() { - try { - isNavigableCache.clear(); - readableLinksCache.clear(); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void buildSelectionToUniqueStrTreeMap() { - TreeMap treeMap = new TreeMap<>(); - Map definitionToSelectionMap = linkProvider.getDefinitionToSelectionMap(); - Map> referenceToSelectionsMap = linkProvider.getReferenceToSelectionsMap(); - - for (String key : definitionToSelectionMap.keySet()) { - Selection selection = definitionToSelectionMap.get(key); - treeMap.put(selection, key); - } - for (String key : referenceToSelectionsMap.keySet()) { - for (Selection selection : referenceToSelectionsMap.get(key)) { - treeMap.put(selection, key); - } - } - selectionToUniqueStrTreeMap = treeMap; - } - - private Selection getSelectionForOffset(int offset) { - if (isNavigationLinksValid) { - Selection offsetSelection = new Selection(offset, offset); - Selection floorSelection = selectionToUniqueStrTreeMap.floorKey(offsetSelection); - if (floorSelection != null && floorSelection.from <= offset && floorSelection.to > offset) { - return floorSelection; - } - } - return null; - } - - private String getUniqueStrForOffset(int offset) { - Selection selection = getSelectionForOffset(offset); - if (selection != null) { - String uniqueStr = selectionToUniqueStrTreeMap.get(selection); - if (this.isLinkNavigable(uniqueStr) && this.getLinkDescription(uniqueStr) != null) { - return uniqueStr; - } - } - return null; - } - - private Integer getSelectionFromForOffset(int offset) { - Selection selection = getSelectionForOffset(offset); - if (selection != null) { - return selection.from; - } - return null; - } - - private String getLinkDescriptionForOffset(int offset) { - String uniqueStr = getUniqueStrForOffset(offset); - if (uniqueStr != null) { - String description = this.getLinkDescription(uniqueStr); - if (description != null) { - return description; - } - } - return null; - } - - private boolean isLinkNavigable(String uniqueStr) { - try { - Boolean isNavigableCached = isNavigableCache.get(uniqueStr); - if (isNavigableCached != null) - return isNavigableCached; - - boolean isNavigable = linkProvider.isLinkNavigable(uniqueStr); - isNavigableCache.put(uniqueStr, isNavigable); - return isNavigable; - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - return false; - } - - private String getLinkDescription(String uniqueStr) { - try { - String descriptionCached = readableLinksCache.get(uniqueStr); - if (descriptionCached != null) - return descriptionCached; - - String description = linkProvider.getLinkDescription(uniqueStr); - if (description != null && description.trim().length() > 0) { - readableLinksCache.put(uniqueStr, description); - return description; - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - return null; - } - - private void onNavigationClicked(String clickedReferenceUniqueStr) { - if (isLocallyNavigable(clickedReferenceUniqueStr)) { - onLocalNavigationRequest(clickedReferenceUniqueStr); - } else if (linkProvider.isLinkNavigable(clickedReferenceUniqueStr)) { - onOutboundNavigationRequest(clickedReferenceUniqueStr); - } else { - JLabel label = this.mainWindow.getLabel(); - if (label == null) - return; - String[] linkParts = clickedReferenceUniqueStr.split("\\|"); - if (linkParts.length <= 1) { - label.setText("Cannot navigate: " + clickedReferenceUniqueStr); - return; - } - String destinationTypeStr = linkParts[1]; - label.setText("Cannot navigate: " + destinationTypeStr.replaceAll("/", ".")); - } - } - - private boolean isLocallyNavigable(String uniqueStr) { - return linkProvider.getDefinitionToSelectionMap().keySet().contains(uniqueStr); - } - - private void onLocalNavigationRequest(String uniqueStr) { - try { - Selection selection = linkProvider.getDefinitionToSelectionMap().get(uniqueStr); - doLocalNavigation(selection); - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void doLocalNavigation(Selection selection) { - try { - textArea.requestFocusInWindow(); - if (selection != null) { - textArea.setSelectionStart(selection.from); - textArea.setSelectionEnd(selection.to); - scrollToSelection(selection.from); - } else { - textArea.setSelectionStart(0); - textArea.setSelectionEnd(0); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - - private void scrollToSelection(final int selectionBeginningOffset) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - int fullHeight = textArea.getBounds().height; - int viewportHeight = textArea.getVisibleRect().height; - int viewportLineCount = viewportHeight / textArea.getLineHeight(); - int selectionLineNum = textArea.getLineOfOffset(selectionBeginningOffset); - int upperMarginToScroll = Math.round(viewportLineCount * 0.29f); - int upperLineToSet = selectionLineNum - upperMarginToScroll; - int currentUpperLine = textArea.getVisibleRect().y / textArea.getLineHeight(); - - if (selectionLineNum <= currentUpperLine + 2 - || selectionLineNum >= currentUpperLine + viewportLineCount - 4) { - Rectangle rectToScroll = new Rectangle(); - rectToScroll.x = 0; - rectToScroll.width = 1; - rectToScroll.y = Math.max(upperLineToSet * textArea.getLineHeight(), 0); - rectToScroll.height = Math.min(viewportHeight, fullHeight - rectToScroll.y); - textArea.scrollRectToVisible(rectToScroll); - } - } catch (Exception e) { - Luyten.showExceptionDialog("Exception!", e); - } - } - }); - } - - private void onOutboundNavigationRequest(String uniqueStr) { - mainWindow.onNavigationRequest(uniqueStr); - } - - public void setDecompilerReferences(MetadataSystem metadataSystem, DecompilerSettings settings, - DecompilationOptions decompilationOptions) { - this.metadataSystem = metadataSystem; - this.settings = settings; - this.decompilationOptions = decompilationOptions; - } - - public TypeDefinition getType() { - return type; - } - - public void setType(TypeDefinition type) { - this.type = type; - } - - public boolean isContentValid() { - return isContentValid; - } - - public void invalidateContent() { - try { - this.setContent(""); - } finally { - this.isContentValid = false; - this.isNavigationLinksValid = false; - } - } - - public void resetScrollPosition() { - lastScrollPercent = null; - } - - public void setInitialNavigationLink(String initialNavigationLink) { - this.initialNavigationLink = initialNavigationLink; - } - - public void onAddedToScreen() { - try { - if (initialNavigationLink != null) { - onLocalNavigationRequest(initialNavigationLink); - } else if (isFirstTimeRun) { - // warm up scrolling - isFirstTimeRun = false; - doLocalNavigation(new Selection(0, 0)); - } - } finally { - initialNavigationLink = null; - } - } - - /** - * sun.swing.CachedPainter holds on OpenFile for a while even after - * JTabbedPane.remove(component) - */ - public void close() { - linkProvider = null; - type = null; - invalidateContent(); - clearLinksCache(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((path == null) ? 0 : path.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - OpenFile other = (OpenFile) obj; - if (path == null) { - if (other.path != null) - return false; - } else if (!path.equals(other.path)) - return false; - return true; - } - -} diff --git a/src/us/deathmarine/luyten/RecentFiles.java b/src/us/deathmarine/luyten/RecentFiles.java deleted file mode 100644 index c553d8bd..00000000 --- a/src/us/deathmarine/luyten/RecentFiles.java +++ /dev/null @@ -1,66 +0,0 @@ -package us.deathmarine.luyten; - -import java.io.File; -import java.util.ArrayList; -import java.util.prefs.Preferences; - -public class RecentFiles { - - public static ArrayList paths = new ArrayList<>(); - private static Preferences prefs = Preferences.userNodeForPackage(RecentFiles.class); - - public static int load() { - boolean saveNeeded = false; - - String serializedPaths = prefs.get("recentFiles", null); - - if (serializedPaths == null) return 0; - - // "test","asdf.txt","text2" - for (String path : serializedPaths.split("\",\"")) { - - path = path.replace("\"", ""); - - if (!path.trim().isEmpty() && !new File(path).exists()) saveNeeded = true; - else paths.add(path); - } - - if (saveNeeded) save(); - - return paths.size(); - } - - public static void add(String path) { - if (paths.contains(path)) { - paths.remove(path); - paths.add(path); - return; - } - - if (paths.size() >= 10) paths.remove(0); - paths.add(path); - - save(); - } - - public static void save() { - if (paths.size() == 0) { - prefs.put("recentFiles", ""); - return; - } - - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < paths.size(); i++) { - if (i != 0) sb.append(','); - - /*if (!new File(paths.get(i)).exists()) { - paths.remove(i); - continue; - }*/ - sb.append("\"").append(paths.get(i)).append("\""); - } - - prefs.put("recentFiles", sb.toString()); - } -} \ No newline at end of file diff --git a/src/us/deathmarine/luyten/Selection.java b/src/us/deathmarine/luyten/Selection.java deleted file mode 100644 index fdf507a4..00000000 --- a/src/us/deathmarine/luyten/Selection.java +++ /dev/null @@ -1,41 +0,0 @@ -package us.deathmarine.luyten; - -public class Selection implements Comparable { - public final Integer from; - public final Integer to; - - public Selection(Integer from, Integer to) { - this.from = from; - this.to = to; - } - - @Override - public int compareTo(Selection o) { - return from.compareTo(o.from); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((from == null) ? 0 : from.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Selection other = (Selection) obj; - if (from == null) { - if (other.from != null) - return false; - } else if (!from.equals(other.from)) - return false; - return true; - } -} diff --git a/src/us/deathmarine/luyten/TooLargeFileException.java b/src/us/deathmarine/luyten/TooLargeFileException.java deleted file mode 100644 index 3dc5abbd..00000000 --- a/src/us/deathmarine/luyten/TooLargeFileException.java +++ /dev/null @@ -1,20 +0,0 @@ -package us.deathmarine.luyten; - -import java.text.DecimalFormat; - -public class TooLargeFileException extends Exception { - private static final long serialVersionUID = 6091096838075139962L; - private long size; - - public TooLargeFileException(long size) { - this.size = size; - } - - public String getReadableFileSize() { - if (size <= 0) - return "0"; - final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" }; - int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; - } -} diff --git a/src/us/deathmarine/luyten/TreeNodeUserObject.java b/src/us/deathmarine/luyten/TreeNodeUserObject.java deleted file mode 100644 index 7bed250c..00000000 --- a/src/us/deathmarine/luyten/TreeNodeUserObject.java +++ /dev/null @@ -1,37 +0,0 @@ -package us.deathmarine.luyten; - -public class TreeNodeUserObject { - - private String originalName; - private String displayName; - - public TreeNodeUserObject(String name) { - this(name, name); - } - - public TreeNodeUserObject(String originalName, String displayName) { - this.originalName = originalName; - this.displayName = displayName; - } - - public String getOriginalName() { - return originalName; - } - - public void setOriginalName(String originalName) { - this.originalName = originalName; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - @Override - public String toString() { - return displayName; - } -} diff --git a/src/us/deathmarine/luyten/TreeUtil.java b/src/us/deathmarine/luyten/TreeUtil.java deleted file mode 100644 index 4fdb95a1..00000000 --- a/src/us/deathmarine/luyten/TreeUtil.java +++ /dev/null @@ -1,81 +0,0 @@ -package us.deathmarine.luyten; - -import java.util.HashSet; -import java.util.Set; -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreePath; - -public class TreeUtil { - - private JTree tree; - - public TreeUtil() { - } - - public TreeUtil(JTree tree) { - this.tree = tree; - } - - public Set getExpansionState() { - Set openedSet = new HashSet<>(); - if (tree != null) { - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - String rowPathStr = getRowPathStr(path); - // for switching Package Explorer on/off - openedSet.addAll(getAllParentPathsStr(rowPathStr)); - } - } - } - return openedSet; - } - - private Set getAllParentPathsStr(String rowPathStr) { - Set parents = new HashSet<>(); - parents.add(rowPathStr); - if (rowPathStr.contains("/")) { - String[] pathElements = rowPathStr.split("/"); - String path = ""; - for (String pathElement : pathElements) { - path = path + pathElement + "/"; - parents.add(path); - } - } - return parents; - } - - public void restoreExpanstionState(Set expansionState) { - if (tree != null && expansionState != null) { - // tree.getRowCount() changes at tree.expandRow() - for (int i = 0; i < tree.getRowCount(); i++) { - TreePath path = tree.getPathForRow(i); - if (expansionState.contains(getRowPathStr(path))) { - tree.expandRow(i); - } - } - } - } - - private String getRowPathStr(TreePath trp) { - String pathStr = ""; - if (trp.getPathCount() > 1) { - for (int i = 1; i < trp.getPathCount(); i++) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) trp.getPathComponent(i); - TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject(); - pathStr = pathStr + userObject.getOriginalName() + "/"; - } - } - return pathStr; - } - - public JTree getTree() { - return tree; - } - - public void setTree(JTree tree) { - this.tree = tree; - } -} diff --git a/src/us/deathmarine/luyten/WindowPosition.java b/src/us/deathmarine/luyten/WindowPosition.java deleted file mode 100644 index 8c70640e..00000000 --- a/src/us/deathmarine/luyten/WindowPosition.java +++ /dev/null @@ -1,93 +0,0 @@ -package us.deathmarine.luyten; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Toolkit; -import javax.swing.JDialog; -import javax.swing.JFrame; - -public class WindowPosition { - - private boolean isFullScreen; - private int windowWidth; - private int windowHeight; - private int windowX; - private int windowY; - - public void readPositionFromWindow(JFrame window) { - isFullScreen = (window.getExtendedState() == JFrame.MAXIMIZED_BOTH); - if (!isFullScreen) { - this.readPositionFromComponent(window); - } - } - - public void readPositionFromDialog(JDialog dialog) { - this.readPositionFromComponent(dialog); - } - - private void readPositionFromComponent(Component component) { - isFullScreen = false; - windowWidth = component.getWidth(); - windowHeight = component.getHeight(); - windowX = component.getX(); - windowY = component.getY(); - } - - public boolean isSavedWindowPositionValid() { - if (isFullScreen) { - return true; - } - if (windowWidth < 100 || windowHeight < 100) { - return false; - } - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - if (windowWidth > screenSize.width + 50 || windowHeight > screenSize.height + 50) { - return false; - } - if (windowY < -20 || windowY > screenSize.height - 50 || windowX < 50 - windowWidth - || windowX > screenSize.width - 50) { - return false; - } - return true; - } - - public boolean isFullScreen() { - return isFullScreen; - } - - public void setFullScreen(boolean isFullScreen) { - this.isFullScreen = isFullScreen; - } - - public int getWindowWidth() { - return windowWidth; - } - - public void setWindowWidth(int windowWidth) { - this.windowWidth = windowWidth; - } - - public int getWindowHeight() { - return windowHeight; - } - - public void setWindowHeight(int windowHeight) { - this.windowHeight = windowHeight; - } - - public int getWindowX() { - return windowX; - } - - public void setWindowX(int windowX) { - this.windowX = windowX; - } - - public int getWindowY() { - return windowY; - } - - public void setWindowY(int windowY) { - this.windowY = windowY; - } -}