Repository: java-decompiler/jd-gui Branch: master Commit: b3c1ced04e57 Files: 257 Total size: 945.3 KB Directory structure: gitextract_0mx2o5ea/ ├── .gitattributes ├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── api/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── jd/ │ └── gui/ │ ├── api/ │ │ ├── API.java │ │ ├── feature/ │ │ │ ├── ContainerEntryGettable.java │ │ │ ├── ContentCopyable.java │ │ │ ├── ContentIndexable.java │ │ │ ├── ContentSavable.java │ │ │ ├── ContentSearchable.java │ │ │ ├── ContentSelectable.java │ │ │ ├── FocusedTypeGettable.java │ │ │ ├── IndexesChangeListener.java │ │ │ ├── LineNumberNavigable.java │ │ │ ├── PageChangeListener.java │ │ │ ├── PageChangeable.java │ │ │ ├── PageClosable.java │ │ │ ├── PageCreator.java │ │ │ ├── PreferencesChangeListener.java │ │ │ ├── SourcesSavable.java │ │ │ ├── TreeNodeExpandable.java │ │ │ ├── UriGettable.java │ │ │ └── UriOpenable.java │ │ └── model/ │ │ ├── Container.java │ │ ├── Indexes.java │ │ ├── TreeNodeData.java │ │ └── Type.java │ └── spi/ │ ├── ContainerFactory.java │ ├── ContextualActionsFactory.java │ ├── FileLoader.java │ ├── Indexer.java │ ├── PanelFactory.java │ ├── PasteHandler.java │ ├── PreferencesPanel.java │ ├── SourceLoader.java │ ├── SourceSaver.java │ ├── TreeNodeFactory.java │ ├── TypeFactory.java │ └── UriLoader.java ├── app/ │ ├── build.gradle │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── jd/ │ │ └── gui/ │ │ ├── App.java │ │ ├── Constants.java │ │ ├── OsxApp.java │ │ ├── controller/ │ │ │ ├── AboutController.java │ │ │ ├── GoToController.java │ │ │ ├── MainController.java │ │ │ ├── OpenTypeController.java │ │ │ ├── OpenTypeHierarchyController.java │ │ │ ├── PreferencesController.java │ │ │ ├── SaveAllSourcesController.java │ │ │ ├── SearchInConstantPoolsController.java │ │ │ └── SelectLocationController.java │ │ ├── model/ │ │ │ ├── configuration/ │ │ │ │ └── Configuration.java │ │ │ ├── container/ │ │ │ │ └── DelegatingFilterContainer.java │ │ │ └── history/ │ │ │ └── History.java │ │ ├── service/ │ │ │ ├── actions/ │ │ │ │ └── ContextualActionsFactoryService.java │ │ │ ├── configuration/ │ │ │ │ ├── ConfigurationPersister.java │ │ │ │ ├── ConfigurationPersisterService.java │ │ │ │ └── ConfigurationXmlPersisterProvider.java │ │ │ ├── container/ │ │ │ │ └── ContainerFactoryService.java │ │ │ ├── extension/ │ │ │ │ └── ExtensionService.java │ │ │ ├── fileloader/ │ │ │ │ └── FileLoaderService.java │ │ │ ├── indexer/ │ │ │ │ └── IndexerService.java │ │ │ ├── mainpanel/ │ │ │ │ ├── ContainerPanelFactoryProvider.java │ │ │ │ └── PanelFactoryService.java │ │ │ ├── pastehandler/ │ │ │ │ └── PasteHandlerService.java │ │ │ ├── platform/ │ │ │ │ └── PlatformService.java │ │ │ ├── preferencespanel/ │ │ │ │ ├── PreferencesPanelService.java │ │ │ │ ├── UISingleInstancePreferencesProvider.java │ │ │ │ └── UITabsPreferencesProvider.java │ │ │ ├── sourceloader/ │ │ │ │ └── SourceLoaderService.java │ │ │ ├── sourcesaver/ │ │ │ │ └── SourceSaverService.java │ │ │ ├── treenode/ │ │ │ │ └── TreeNodeFactoryService.java │ │ │ ├── type/ │ │ │ │ └── TypeFactoryService.java │ │ │ └── uriloader/ │ │ │ └── UriLoaderService.java │ │ ├── util/ │ │ │ ├── exception/ │ │ │ │ └── ExceptionUtil.java │ │ │ ├── function/ │ │ │ │ └── TriConsumer.java │ │ │ ├── net/ │ │ │ │ ├── InterProcessCommunicationUtil.java │ │ │ │ └── UriUtil.java │ │ │ └── swing/ │ │ │ └── SwingUtil.java │ │ └── view/ │ │ ├── AboutView.java │ │ ├── GoToView.java │ │ ├── MainView.java │ │ ├── OpenTypeHierarchyView.java │ │ ├── OpenTypeView.java │ │ ├── PreferencesView.java │ │ ├── SaveAllSourcesView.java │ │ ├── SearchInConstantPoolsView.java │ │ ├── SelectLocationView.java │ │ ├── bean/ │ │ │ └── OpenTypeListCellBean.java │ │ ├── component/ │ │ │ ├── IconButton.java │ │ │ ├── List.java │ │ │ ├── Tree.java │ │ │ └── panel/ │ │ │ ├── MainTabbedPanel.java │ │ │ ├── TabbedPanel.java │ │ │ └── TreeTabbedPanel.java │ │ └── renderer/ │ │ ├── OpenTypeListCellRenderer.java │ │ └── TreeNodeRenderer.java │ └── resources/ │ └── META-INF/ │ └── services/ │ ├── org.jd.gui.spi.PanelFactory │ └── org.jd.gui.spi.PreferencesPanel ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── services/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── antlr/ │ │ │ └── Java.g4 │ │ ├── java/ │ │ │ └── org/ │ │ │ ├── fife/ │ │ │ │ └── ui/ │ │ │ │ └── rtextarea/ │ │ │ │ └── Marker.java │ │ │ └── jd/ │ │ │ └── gui/ │ │ │ ├── model/ │ │ │ │ └── container/ │ │ │ │ ├── ContainerEntryComparator.java │ │ │ │ ├── EarContainer.java │ │ │ │ ├── GenericContainer.java │ │ │ │ ├── JarContainer.java │ │ │ │ ├── JavaModuleContainer.java │ │ │ │ ├── KarContainer.java │ │ │ │ └── WarContainer.java │ │ │ ├── service/ │ │ │ │ ├── actions/ │ │ │ │ │ ├── CopyQualifiedNameContextualActionsFactory.java │ │ │ │ │ └── InvalidFormatException.java │ │ │ │ ├── container/ │ │ │ │ │ ├── EarContainerFactoryProvider.java │ │ │ │ │ ├── GenericContainerFactoryProvider.java │ │ │ │ │ ├── JarContainerFactoryProvider.java │ │ │ │ │ ├── JavaModuleContainerFactoryProvider.java │ │ │ │ │ ├── KarContainerFactoryProvider.java │ │ │ │ │ └── WarContainerFactoryProvider.java │ │ │ │ ├── fileloader/ │ │ │ │ │ ├── AarFileLoaderProvider.java │ │ │ │ │ ├── AbstractFileLoaderProvider.java │ │ │ │ │ ├── AbstractTypeFileLoaderProvider.java │ │ │ │ │ ├── ClassFileLoaderProvider.java │ │ │ │ │ ├── EarFileLoaderProvider.java │ │ │ │ │ ├── JarFileLoaderProvider.java │ │ │ │ │ ├── JavaFileLoaderProvider.java │ │ │ │ │ ├── JavaModuleFileLoaderProvider.java │ │ │ │ │ ├── KarFileLoaderProvider.java │ │ │ │ │ ├── LogFileLoaderProvider.java │ │ │ │ │ ├── WarFileLoaderProvider.java │ │ │ │ │ └── ZipFileLoaderProvider.java │ │ │ │ ├── indexer/ │ │ │ │ │ ├── AbstractIndexerProvider.java │ │ │ │ │ ├── ClassFileIndexerProvider.java │ │ │ │ │ ├── DirectoryIndexerProvider.java │ │ │ │ │ ├── EjbJarXmlFileIndexerProvider.java │ │ │ │ │ ├── JavaFileIndexerProvider.java │ │ │ │ │ ├── JavaModuleFileIndexerProvider.java │ │ │ │ │ ├── JavaModuleInfoFileIndexerProvider.java │ │ │ │ │ ├── MetainfServiceFileIndexerProvider.java │ │ │ │ │ ├── TextFileIndexerProvider.java │ │ │ │ │ ├── WebXmlFileIndexerProvider.java │ │ │ │ │ ├── XmlBasedFileIndexerProvider.java │ │ │ │ │ ├── XmlFileIndexerProvider.java │ │ │ │ │ └── ZipFileIndexerProvider.java │ │ │ │ ├── pastehandler/ │ │ │ │ │ └── LogPasteHandler.java │ │ │ │ ├── preferencespanel/ │ │ │ │ │ ├── ClassFileDecompilerPreferencesProvider.java │ │ │ │ │ ├── ClassFileSaverPreferencesProvider.java │ │ │ │ │ ├── DirectoryIndexerPreferencesProvider.java │ │ │ │ │ ├── MavenOrgSourceLoaderPreferencesProvider.java │ │ │ │ │ └── ViewerPreferencesProvider.java │ │ │ │ ├── sourceloader/ │ │ │ │ │ └── MavenOrgSourceLoaderProvider.java │ │ │ │ ├── sourcesaver/ │ │ │ │ │ ├── AbstractSourceSaverProvider.java │ │ │ │ │ ├── ClassFileSourceSaverProvider.java │ │ │ │ │ ├── DirectorySourceSaverProvider.java │ │ │ │ │ ├── FileSourceSaverProvider.java │ │ │ │ │ ├── PackageSourceSaverProvider.java │ │ │ │ │ └── ZipFileSourceSaverProvider.java │ │ │ │ ├── treenode/ │ │ │ │ │ ├── AbstractTreeNodeFactoryProvider.java │ │ │ │ │ ├── AbstractTypeFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── ClassFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── ClassesDirectoryTreeNodeFactoryProvider.java │ │ │ │ │ ├── CssFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── DirectoryTreeNodeFactoryProvider.java │ │ │ │ │ ├── DtdFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── EarFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── EjbJarXmlFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── FileTreeNodeFactoryProvider.java │ │ │ │ │ ├── HtmlFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── ImageFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JarFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JavaFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JavaModuleFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JavaModulePackageTreeNodeFactoryProvider.java │ │ │ │ │ ├── JavascriptFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JsonFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── JspFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── KarFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── ManifestFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── MetainfDirectoryTreeNodeFactoryProvider.java │ │ │ │ │ ├── MetainfServiceFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── ModuleInfoFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── PackageTreeNodeFactoryProvider.java │ │ │ │ │ ├── PropertiesFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── SpiFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── SqlFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── TextFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── WarFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── WarPackageTreeNodeFactoryProvider.java │ │ │ │ │ ├── WebXmlFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── WebinfLibDirectoryTreeNodeFactoryProvider.java │ │ │ │ │ ├── XmlBasedFileTreeNodeFactoryProvider.java │ │ │ │ │ ├── XmlFileTreeNodeFactoryProvider.java │ │ │ │ │ └── ZipFileTreeNodeFactoryProvider.java │ │ │ │ ├── type/ │ │ │ │ │ ├── AbstractTypeFactoryProvider.java │ │ │ │ │ ├── ClassFileTypeFactoryProvider.java │ │ │ │ │ └── JavaFileTypeFactoryProvider.java │ │ │ │ └── uriloader/ │ │ │ │ └── FileUriLoaderProvider.java │ │ │ ├── util/ │ │ │ │ ├── container/ │ │ │ │ │ └── JarContainerEntryUtil.java │ │ │ │ ├── decompiler/ │ │ │ │ │ ├── ClassPathLoader.java │ │ │ │ │ ├── ContainerLoader.java │ │ │ │ │ ├── LineNumberStringBuilderPrinter.java │ │ │ │ │ ├── NopPrinter.java │ │ │ │ │ └── StringBuilderPrinter.java │ │ │ │ ├── exception/ │ │ │ │ │ └── ExceptionUtil.java │ │ │ │ ├── index/ │ │ │ │ │ └── IndexesUtil.java │ │ │ │ ├── io/ │ │ │ │ │ ├── NewlineOutputStream.java │ │ │ │ │ └── TextReader.java │ │ │ │ ├── matcher/ │ │ │ │ │ └── DescriptorMatcher.java │ │ │ │ ├── parser/ │ │ │ │ │ └── antlr/ │ │ │ │ │ ├── ANTLRJavaParser.java │ │ │ │ │ └── AbstractJavaListener.java │ │ │ │ └── xml/ │ │ │ │ └── AbstractXmlPathFinder.java │ │ │ └── view/ │ │ │ ├── component/ │ │ │ │ ├── AbstractTextPage.java │ │ │ │ ├── ClassFilePage.java │ │ │ │ ├── CustomLineNumbersPage.java │ │ │ │ ├── DynamicPage.java │ │ │ │ ├── EjbJarXmlFilePage.java │ │ │ │ ├── HyperlinkPage.java │ │ │ │ ├── JavaFilePage.java │ │ │ │ ├── LogPage.java │ │ │ │ ├── ManifestFilePage.java │ │ │ │ ├── ModuleInfoFilePage.java │ │ │ │ ├── OneTypeReferencePerLinePage.java │ │ │ │ ├── RoundMarkErrorStrip.java │ │ │ │ ├── TextPage.java │ │ │ │ ├── TypePage.java │ │ │ │ ├── TypeReferencePage.java │ │ │ │ ├── WebXmlFilePage.java │ │ │ │ └── XmlFilePage.java │ │ │ └── data/ │ │ │ └── TreeNodeBean.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── org.jd.gui.spi.ContainerFactory │ │ │ ├── org.jd.gui.spi.ContextualActionsFactory │ │ │ ├── org.jd.gui.spi.FileLoader │ │ │ ├── org.jd.gui.spi.Indexer │ │ │ ├── org.jd.gui.spi.PasteHandler │ │ │ ├── org.jd.gui.spi.PreferencesPanel │ │ │ ├── org.jd.gui.spi.SourceLoader │ │ │ ├── org.jd.gui.spi.SourceSaver │ │ │ ├── org.jd.gui.spi.TreeNodeFactory │ │ │ ├── org.jd.gui.spi.TypeFactory │ │ │ └── org.jd.gui.spi.UriLoader │ │ └── rsyntaxtextarea/ │ │ ├── RSyntaxTextArea_License.txt │ │ └── themes/ │ │ └── eclipse.xml │ └── test/ │ └── java/ │ └── org/ │ └── jd/ │ └── gui/ │ ├── util/ │ │ └── matcher/ │ │ └── DescriptorMatcherTest.java │ └── view/ │ └── component/ │ ├── ClassFilePageTest.java │ └── JavaFilePageTest.java ├── settings.gradle └── src/ ├── linux/ │ └── resources/ │ └── jd-gui.desktop ├── osx/ │ ├── dist/ │ │ └── JD-GUI.app/ │ │ └── Contents/ │ │ └── Resources/ │ │ └── jd-gui.icns │ └── resources/ │ ├── Info.plist │ └── universalJavaApplicationStub.sh └── proguard/ └── resources/ └── proguard.config.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Set the default behavior, in case people don't have core.autocrlf set. * text=auto # Declare OSX files that will always have LF line endings on checkout. Info.plist text eol=lf # Declare script files that will always have LF line endings on checkout. *.sh text eol=lf # Declare script files that will always have CR/LF line endings on checkout. *.bat text eol=crlf # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary *.gif binary *.icns binary ================================================ FILE: .gitignore ================================================ # Java *.class # JD debug* # JD-GUI src-generated/ jd-gui.cfg # Idea .idea/ out/ *.ipr *.iml *.iws # Eclipse .settings/ classes/ .classpath .project # Mac .DS_Store #Windows Thumbs.db # Maven log/ target/ # Gradle .gradle/ build/ !gradle/wrapper/* # WinMerge *.bak ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. JD-GUI, a standalone graphical utility that displays Java sources from CLASS files Copyright (C) 2008-2019 Emmanuel Dupuy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: JD-GUI Copyright (C) 2008-2019 Emmanuel Dupuy This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: NOTICE ================================================ JD-GUI license - GPLv3 Libraries used: Groovy - Apache License 2.0 Gradle - Apache License 2.0 JD-Core Java Release - GPLv3 RSyntaxTextArea - Modified BSD license JD-GUI Mac OSX distribution: universalJavaApplicationStub - MIT License JD-GUI Windows distribution: Launch4j - MIT License ================================================ FILE: README.md ================================================ # JD-GUI JD-GUI, a standalone graphical utility that displays Java sources from CLASS files. ![](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/jd-gui.png) - Java Decompiler projects home page: [http://java-decompiler.github.io](http://java-decompiler.github.io) - JD-GUI source code: [https://github.com/java-decompiler/jd-gui](https://github.com/java-decompiler/jd-gui) ## Description JD-GUI is a standalone graphical utility that displays Java source codes of ".class" files. You can browse the reconstructed source code with the JD-GUI for instant access to methods and fields. ## How to build JD-GUI ? ``` > git clone https://github.com/java-decompiler/jd-gui.git > cd jd-gui > ./gradlew build ``` generate : - _"build/libs/jd-gui-x.y.z.jar"_ - _"build/libs/jd-gui-x.y.z-min.jar"_ - _"build/distributions/jd-gui-windows-x.y.z.zip"_ - _"build/distributions/jd-gui-osx-x.y.z.tar"_ - _"build/distributions/jd-gui-x.y.z.deb"_ - _"build/distributions/jd-gui-x.y.z.rpm"_ ## How to launch JD-GUI ? - Double-click on _"jd-gui-x.y.z.jar"_ - Double-click on _"jd-gui.exe"_ application from Windows - Double-click on _"JD-GUI"_ application from Mac OSX - Execute _"java -jar jd-gui-x.y.z.jar"_ or _"java -classpath jd-gui-x.y.z.jar org.jd.gui.App"_ ## How to use JD-GUI ? - Open a file with menu "File > Open File..." - Open recent files with menu "File > Recent Files" - Drag and drop files from your file explorer ## How to extend JD-GUI ? ``` > ./gradlew idea ``` generate Idea Intellij project ``` > ./gradlew eclipse ``` generate Eclipse project ``` > java -classpath jd-gui-x.y.z.jar;myextension1.jar;myextension2.jar org.jd.gui.App ``` launch JD-GUI with your extensions ## How to uninstall JD-GUI ? - Java: Delete "jd-gui-x.y.z.jar" and "jd-gui.cfg". - Mac OSX: Drag and drop "JD-GUI" application into the trash. - Windows: Delete "jd-gui.exe" and "jd-gui.cfg". ## License Released under the [GNU GPL v3](LICENSE). ## Donations Did JD-GUI help you to solve a critical situation? Do you use JD-Eclipse daily? What about making a donation? [![paypal](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/btn_donate_euro.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=C88ZMVZ78RF22) [![paypal](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/btn_donate_usd.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CRMXT4Y4QLQGU) ================================================ FILE: api/build.gradle ================================================ apply plugin: 'java' version = '1.0.0' ================================================ FILE: api/src/main/java/org/jd/gui/api/API.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.*; import javax.swing.*; import java.io.File; import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.Map; import java.util.concurrent.Future; public interface API { boolean openURI(URI uri); boolean openURI(int x, int y, Collection entries, String query, String fragment); void addURI(URI uri); void addPanel(String title, Icon icon, String tip, T component); Collection getContextualActions(Container.Entry entry, String fragment); UriLoader getUriLoader(URI uri); FileLoader getFileLoader(File file); ContainerFactory getContainerFactory(Path rootPath); PanelFactory getMainPanelFactory(Container container); TreeNodeFactory getTreeNodeFactory(Container.Entry entry); TypeFactory getTypeFactory(Container.Entry entry); Indexer getIndexer(Container.Entry entry); SourceSaver getSourceSaver(Container.Entry entry); Map getPreferences(); Collection> getCollectionOfFutureIndexes(); interface LoadSourceListener { void sourceLoaded(String source); } String getSource(Container.Entry entry); void loadSource(Container.Entry entry, LoadSourceListener listener); File loadSourceFile(Container.Entry entry); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContainerEntryGettable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.model.Container; public interface ContainerEntryGettable { Container.Entry getEntry(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContentCopyable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface ContentCopyable { void copy(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContentIndexable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.API; import org.jd.gui.api.model.Indexes; public interface ContentIndexable { Indexes index(API api); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContentSavable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.API; import java.io.OutputStream; public interface ContentSavable { String getFileName(); void save(API api, OutputStream os); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContentSearchable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface ContentSearchable { boolean highlightText(String text, boolean caseSensitive); void findNext(String text, boolean caseSensitive); void findPrevious(String text, boolean caseSensitive); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/ContentSelectable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface ContentSelectable { void selectAll(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/FocusedTypeGettable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface FocusedTypeGettable extends ContainerEntryGettable { String getFocusedTypeName(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/IndexesChangeListener.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.model.Indexes; import java.util.Collection; import java.util.concurrent.Future; public interface IndexesChangeListener { void indexesChanged(Collection> collectionOfFutureIndexes); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/LineNumberNavigable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface LineNumberNavigable { int getMaximumLineNumber(); void goToLineNumber(int lineNumber); boolean checkLineNumber(int lineNumber); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/PageChangeListener.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import javax.swing.*; public interface PageChangeListener { void pageChanged(T page); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/PageChangeable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface PageChangeable { void addPageChangeListener(PageChangeListener listener); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/PageClosable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; public interface PageClosable { boolean closePage(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/PageCreator.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.API; import javax.swing.*; public interface PageCreator { T createPage(API api); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/PreferencesChangeListener.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import java.util.Map; public interface PreferencesChangeListener { void preferencesChanged(Map preferences); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/SourcesSavable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.API; import java.nio.file.Path; public interface SourcesSavable { String getSourceFileName(); int getFileCount(); void save(API api, Controller controller, Listener listener, Path path); interface Controller { boolean isCancelled(); } interface Listener { void pathSaved(Path path); } } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/TreeNodeExpandable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import org.jd.gui.api.API; public interface TreeNodeExpandable { void populateTreeNode(API api); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/UriGettable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import java.net.URI; public interface UriGettable { URI getUri(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/feature/UriOpenable.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.feature; import java.net.URI; /** * uri : scheme '://' path ('?' query)? ('#' fragment)?
* scheme : 'generic' | 'jar' | 'war' | 'ear' | 'dex' | ...
* path : singlePath('!' singlePath)*
* singlePath : [path/to/dir/] | [path/to/file]
* query : queryLineNumber | queryPosition | querySearch
* queryLineNumber : 'lineNumber=' [numeric]
* queryPosition : 'position=' [numeric]
* querySearch : 'highlightPattern=' queryPattern '&highlightFlags=' queryFlags ('&highlightScope=' typeName)?
* queryPattern : [start of string] | [start of type name] | [start of field name] | [start of method name]
* queryFlags : 'd'? // Match declarations
* 'r'? // Match references
* 't'? // Match types
* 'c'? // Match constructors
* 'm'? // Match methods
* 'f'? // Match fields
* 's'? // Match strings
* fragment : fragmentType | fragmentField | fragmentMethod
* fragmentType : typeName
* fragmentField : typeName '-' [field name] '-' descriptor
* fragmentMethod : typeName '-' [method name] '-' methodDescriptor
* methodDescriptor : '(*)?' | // Match all method descriptors
* '(' descriptor* ')' descriptor
* descriptor : '?' | // Match a primitive or a type name
* '['* primitiveOrTypeName
* primitiveOrTypeName : 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'L' typeName ';' | 'S' | 'Z'
* typeName : [internal qualified name] | '*\/' [name]
*
* Examples:
*
    *
  • file://dir1/dir2/
  • *
  • file://dir1/dir2/file
  • *
  • jar://dir1/dir2/
  • *
  • jar://dir1/dir2/file
  • * *
  • jar://dir1/dir2/javafile
  • *
  • jar://dir1/dir2/javafile#type
  • *
  • jar://dir1/dir2/javafile#type-fieldName-descriptor
  • *
  • jar://dir1/dir2/javafile#type-methodName-descriptor
  • *
  • jar://dir1/dir2/javafile#innertype
  • *
  • jar://dir1/dir2/javafile#innertype-fieldName-?
  • *
  • jar://dir1/dir2/javafile#innertype-methodName-(*)?
  • *
  • jar://dir1/dir2/javafile#innertype-methodName-(?JZLjava/lang/Sting;C)I
  • *
  • jar://dir1/dir2/javafile#innertype-fieldName-descriptor
  • *
  • jar://dir1/dir2/javafile#innertype-methodName-descriptor
  • * *
  • file://dir1/dir2/file?lineNumber=numeric
  • *
  • file://dir1/dir2/file?position=numeric
  • *
  • file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=java/lang/String
  • *
  • file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=*\/String
  • *
*/ public interface UriOpenable { boolean openUri(URI uri); } ================================================ FILE: api/src/main/java/org/jd/gui/api/model/Container.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.model; import java.io.InputStream; import java.net.URI; import java.util.Collection; public interface Container { String getType(); Entry getRoot(); /** * File or directory */ interface Entry { Container getContainer(); Entry getParent(); URI getUri(); String getPath(); boolean isDirectory(); long length(); InputStream getInputStream(); Collection getChildren(); } } ================================================ FILE: api/src/main/java/org/jd/gui/api/model/Indexes.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.model; import java.util.Collection; import java.util.Map; /** * Whatever the language/file format (Java|Groovy|Scala/Class|DEX, Java|Javascript/Source, C#/CIL, ...), type names, * stored in the indexes, use the JVM internal format (package separator = '/', inner class separator = '$').
*
* List of default indexes: *
    *
  • * Map "strings"
    * key: a string
    * value: a list of entries containing the string *
  • *
  • * Map "typeDeclarations"
    * key: a type name using internal JVM internal format
    * value: a list of entries containing the type declaration *
  • *
  • * Map "constructorDeclarations"
    * key: a type name using internal JVM internal format
    * value: a list of entries containing the constructor declaration *
  • *
  • * Map "constructorReferences"
    * key: a type name using internal JVM internal format
    * value: a list of entries containing the constructor reference *
  • *
  • * Map "methodDeclarations"
    * key: a method name
    * value: a list of entries containing the method declaration *
  • *
  • * Map "methodReferences"
    * key: a method name
    * value: a list of entries containing the method reference *
  • *
  • * Map "fieldDeclarations"
    * key: a field name
    * value: a list of entries containing the field declaration *
  • *
  • * Map "fieldReferences"
    * key: a field name
    * value: a list of entries containing the field reference *
  • *
  • * Map "subTypeNames"
    * key: a super type name using internal JVM internal format
    * value: a list of sub type names using internal JVM internal format *
  • *
*/ public interface Indexes { Map getIndex(String name); } ================================================ FILE: api/src/main/java/org/jd/gui/api/model/TreeNodeData.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.model; import javax.swing.*; public interface TreeNodeData { String getLabel(); String getTip(); Icon getIcon(); Icon getOpenIcon(); } ================================================ FILE: api/src/main/java/org/jd/gui/api/model/Type.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.api.model; import javax.swing.*; import java.util.Collection; public interface Type { int FLAG_PUBLIC = 1; int FLAG_PRIVATE = 2; int FLAG_PROTECTED = 4; int FLAG_STATIC = 8; int FLAG_FINAL = 16; int FLAG_VARARGS = 128; int FLAG_INTERFACE = 512; int FLAG_ABSTRACT = 1024; int FLAG_ANNOTATION = 8192; int FLAG_ENUM = 16384; int getFlags(); String getName(); String getSuperName(); String getOuterName(); String getDisplayTypeName(); String getDisplayInnerTypeName(); String getDisplayPackageName(); Icon getIcon(); Collection getInnerTypes(); Collection getFields(); Collection getMethods(); interface Field { int getFlags(); String getName(); String getDescriptor(); String getDisplayName(); Icon getIcon(); } interface Method { int getFlags(); String getName(); String getDescriptor(); String getDisplayName(); Icon getIcon(); } } ================================================ FILE: api/src/main/java/org/jd/gui/spi/ContainerFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public interface ContainerFactory { String getType(); boolean accept(API api, Path rootPath); Container make(API api, Container.Entry parentEntry, Path rootPath); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/ContextualActionsFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import javax.swing.*; import java.util.Collection; public interface ContextualActionsFactory { String GROUP_NAME = "GroupNameKey"; /** * Build a collection of actions for 'entry' and 'fragment', grouped by GROUP_NAME and sorted by NAME. Null values * are added for separators. * * @param fragment @see jd.gui.api.feature.UriOpenable * @return a collection of actions */ Collection make(API api, Container.Entry entry, String fragment); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/FileLoader.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import java.io.File; public interface FileLoader { String[] getExtensions(); String getDescription(); boolean accept(API api, File file); boolean load(API api, File file); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/Indexer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import java.util.regex.Pattern; public interface Indexer { String[] getSelectors(); Pattern getPathPattern(); void index(API api, Container.Entry entry, Indexes indexes); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/PanelFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import javax.swing.*; public interface PanelFactory { String[] getTypes(); T make(API api, Container container); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/PasteHandler.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; public interface PasteHandler { boolean accept(Object obj); void paste(API api, Object obj); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/PreferencesPanel.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import javax.swing.*; import java.awt.*; import java.util.Map; public interface PreferencesPanel { String getPreferencesGroupTitle(); String getPreferencesPanelTitle(); JComponent getPanel(); void init(Color errorBackgroundColor); boolean isActivated(); void loadPreferences(Map preferences); void savePreferences(Map preferences); boolean arePreferencesValid(); void addPreferencesChangeListener(PreferencesPanelChangeListener listener); interface PreferencesPanelChangeListener { void preferencesPanelChanged(PreferencesPanel source); } } ================================================ FILE: api/src/main/java/org/jd/gui/spi/SourceLoader.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.io.File; public interface SourceLoader { String getSource(API api, Container.Entry entry); String loadSource(API api, Container.Entry entry); File loadSourceFile(API api, Container.Entry entry); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/SourceSaver.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; import java.util.regex.Pattern; public interface SourceSaver { String[] getSelectors(); Pattern getPathPattern(); String getSourcePath(Container.Entry entry); int getFileCount(API api, Container.Entry entry); /** * Check parent path, build source file name, create NIO path and save the content. */ void save(API api, Controller controller, Listener listener, Path rootPath, Container.Entry entry); /** * Save content: *
    *
  • For file, save the source content.
  • *
  • For directory, call 'save' for each children.
  • *
*/ void saveContent(API api, Controller controller, Listener listener, Path rootPath, Path path, Container.Entry entry); interface Controller { boolean isCancelled(); } interface Listener { void pathSaved(Path path); } } ================================================ FILE: api/src/main/java/org/jd/gui/spi/TreeNodeFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import javax.swing.tree.DefaultMutableTreeNode; import java.util.regex.Pattern; public interface TreeNodeFactory { String[] getSelectors(); Pattern getPathPattern(); T make(API api, Container.Entry entry); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/TypeFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import java.util.Collection; import java.util.regex.Pattern; public interface TypeFactory { String[] getSelectors(); Pattern getPathPattern(); /** * @return all root types contains in 'entry' */ Collection make(API api, Container.Entry entry); /** * @param fragment @see jd.gui.api.feature.UriOpenable * @return if 'fragment' is null, return the main type in 'entry', * otherwise, return the type or sub-type matching with 'fragment' */ Type make(API api, Container.Entry entry, String fragment); } ================================================ FILE: api/src/main/java/org/jd/gui/spi/UriLoader.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.spi; import org.jd.gui.api.API; import java.net.URI; public interface UriLoader { String[] getSchemes(); boolean accept(API api, URI uri); boolean load(API api, URI uri); } ================================================ FILE: app/build.gradle ================================================ apply plugin: 'java' dependencies { provided 'com.yuvimasory:orange-extensions:1.3.0' // OSX support compile project(':api') runtime project(':services') } version = parent.version ================================================ FILE: app/src/main/java/org/jd/gui/App.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui; import org.jd.gui.controller.MainController; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.service.configuration.ConfigurationPersister; import org.jd.gui.service.configuration.ConfigurationPersisterService; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.net.InterProcessCommunicationUtil; import javax.swing.*; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class App { protected static final String SINGLE_INSTANCE = "UIMainWindowPreferencesProvider.singleInstance"; protected static MainController controller; public static void main(String[] args) { if (checkHelpFlag(args)) { JOptionPane.showMessageDialog(null, "Usage: jd-gui [option] [input-file] ...\n\nOption:\n -h Show this help message and exit", Constants.APP_NAME, JOptionPane.INFORMATION_MESSAGE); } else { // Load preferences ConfigurationPersister persister = ConfigurationPersisterService.getInstance().get(); Configuration configuration = persister.load(); Runtime.getRuntime().addShutdownHook(new Thread(() -> persister.save(configuration))); if ("true".equals(configuration.getPreferences().get(SINGLE_INSTANCE))) { InterProcessCommunicationUtil ipc = new InterProcessCommunicationUtil(); try { ipc.listen(receivedArgs -> controller.openFiles(newList(receivedArgs))); } catch (Exception notTheFirstInstanceException) { // Send args to main windows and exit ipc.send(args); System.exit(0); } } // Create SwingBuilder, set look and feel try { UIManager.setLookAndFeel(configuration.getLookAndFeel()); } catch (Exception e) { configuration.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); try { UIManager.setLookAndFeel(configuration.getLookAndFeel()); } catch (Exception ee) { assert ExceptionUtil.printStackTrace(ee); } } // Create main controller and show main frame controller = new MainController(configuration); controller.show(newList(args)); } } protected static boolean checkHelpFlag(String[] args) { if (args != null) { for (String arg : args) { if ("-h".equals(arg)) { return true; } } } return false; } protected static List newList(String[] paths) { if (paths == null) { return Collections.emptyList(); } else { ArrayList files = new ArrayList<>(paths.length); for (String path : paths) { files.add(new File(path)); } return files; } } } ================================================ FILE: app/src/main/java/org/jd/gui/Constants.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui; public class Constants { public static final String APP_NAME = "JD-GUI"; public static final int DEFAULT_WIDTH = 600; public static final int DEFAULT_HEIGHT = 400; public static final int MINIMAL_WIDTH = 500; public static final int MINIMAL_HEIGHT = 160; public static final String CONFIG_FILENAME = "jd-gui.cfg"; public static final int MAX_RECENT_FILES = 10; public static final int RECENT_FILE_MAX_LENGTH = 200; } ================================================ FILE: app/src/main/java/org/jd/gui/OsxApp.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui; import com.apple.eawt.Application; public class OsxApp extends App { @SuppressWarnings("unchecked") public static void main(String[] args) { // Create an instance of the mac OSX Application class Application application = Application.getApplication(); App.main(args); // Add an handle invoked when the application is asked to open a list of files application.setOpenFileHandler(e -> controller.openFiles(e.getFiles())); // Add an handle invoked when the application is asked to quit application.setQuitHandler((e, r) -> System.exit(0)); } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/AboutController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.view.AboutView; import javax.swing.*; public class AboutController { protected AboutView aboutView; public AboutController(JFrame mainFrame) { // Create UI aboutView = new AboutView(mainFrame); } public void show() { // Show aboutView.show(); } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/GoToController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.feature.LineNumberNavigable; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.view.GoToView; import javax.swing.*; import java.util.function.IntConsumer; public class GoToController { protected GoToView goToView; public GoToController(Configuration configuration, JFrame mainFrame) { // Create UI goToView = new GoToView(configuration, mainFrame); } public void show(LineNumberNavigable navigator, IntConsumer okCallback) { // Show goToView.show(navigator, okCallback); } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/MainController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.model.history.History; import org.jd.gui.service.actions.ContextualActionsFactoryService; import org.jd.gui.service.container.ContainerFactoryService; import org.jd.gui.service.fileloader.FileLoaderService; import org.jd.gui.service.indexer.IndexerService; import org.jd.gui.service.mainpanel.PanelFactoryService; import org.jd.gui.service.pastehandler.PasteHandlerService; import org.jd.gui.service.platform.PlatformService; import org.jd.gui.service.preferencespanel.PreferencesPanelService; import org.jd.gui.service.sourceloader.SourceLoaderService; import org.jd.gui.service.sourcesaver.SourceSaverService; import org.jd.gui.service.treenode.TreeNodeFactoryService; import org.jd.gui.service.type.TypeFactoryService; import org.jd.gui.service.uriloader.UriLoaderService; import org.jd.gui.spi.*; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.net.UriUtil; import org.jd.gui.util.swing.SwingUtil; import org.jd.gui.view.MainView; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileSystemView; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.nio.file.Path; import java.util.*; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class MainController implements API { protected Configuration configuration; protected MainView mainView; protected GoToController goToController; protected OpenTypeController openTypeController; protected OpenTypeHierarchyController openTypeHierarchyController; protected PreferencesController preferencesController; protected SearchInConstantPoolsController searchInConstantPoolsController; protected SaveAllSourcesController saveAllSourcesController; protected SelectLocationController selectLocationController; protected AboutController aboutController; protected SourceLoaderService sourceLoaderService; protected History history = new History(); protected JComponent currentPage = null; protected ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); protected ArrayList containerChangeListeners = new ArrayList<>(); @SuppressWarnings("unchecked") public MainController(Configuration configuration) { this.configuration = configuration; SwingUtil.invokeLater(() -> { if (PlatformService.getInstance().isLinux()) { // Fix for GTKLookAndFeel SwingUtil.installGtkPopupBugWorkaround(); } // Create main frame mainView = new MainView( configuration, this, history, e -> onOpen(), e -> onClose(), e -> onSaveSource(), e -> onSaveAllSources(), e -> System.exit(0), e -> onCopy(), e -> onPaste(), e -> onSelectAll(), e -> onFind(), e -> onFindPrevious(), e -> onFindNext(), e -> onFindCriteriaChanged(), () -> onFindCriteriaChanged(), e -> onOpenType(), e -> onOpenTypeHierarchy(), e -> onGoTo(), e -> openURI(history.backward()), e -> openURI(history.forward()), e -> onSearch(), e -> onJdWebSite(), e -> onJdGuiIssues(), e -> onJdCoreIssues(), e -> onPreferences(), e -> onAbout(), () -> panelClosed(), page -> onCurrentPageChanged((JComponent)page), file -> openFile((File)file)); }); } // --- Show GUI --- // @SuppressWarnings("unchecked") public void show(List files) { SwingUtil.invokeLater(() -> { // Show main frame mainView.show(configuration.getMainWindowLocation(), configuration.getMainWindowSize(), configuration.isMainWindowMaximize()); if (!files.isEmpty()) { openFiles(files); } }); // Background initializations executor.schedule(() -> { // Background service initialization UriLoaderService.getInstance(); FileLoaderService.getInstance(); ContainerFactoryService.getInstance(); IndexerService.getInstance(); TreeNodeFactoryService.getInstance(); TypeFactoryService.getInstance(); SwingUtil.invokeLater(() -> { // Populate recent files menu mainView.updateRecentFilesMenu(configuration.getRecentFiles()); // Background controller creation JFrame mainFrame = mainView.getMainFrame(); saveAllSourcesController = new SaveAllSourcesController(MainController.this, mainFrame); containerChangeListeners.add(openTypeController = new OpenTypeController(MainController.this, executor, mainFrame)); containerChangeListeners.add(openTypeHierarchyController = new OpenTypeHierarchyController(MainController.this, executor, mainFrame)); goToController = new GoToController(configuration, mainFrame); containerChangeListeners.add(searchInConstantPoolsController = new SearchInConstantPoolsController(MainController.this, executor, mainFrame)); preferencesController = new PreferencesController(configuration, mainFrame, PreferencesPanelService.getInstance().getProviders()); selectLocationController = new SelectLocationController(MainController.this, mainFrame); aboutController = new AboutController(mainFrame); sourceLoaderService = new SourceLoaderService(); // Add listeners mainFrame.addComponentListener(new MainFrameListener(configuration)); // Set drop files transfer handler mainFrame.setTransferHandler(new FilesTransferHandler()); // Background class loading new JFileChooser().addChoosableFileFilter(new FileNameExtensionFilter("", "dummy")); FileSystemView.getFileSystemView().isFileSystemRoot(new File("dummy")); new JLayer(); }); }, 400, TimeUnit.MILLISECONDS); PasteHandlerService.getInstance(); PreferencesPanelService.getInstance(); ContextualActionsFactoryService.getInstance(); SourceSaverService.getInstance(); } // --- Actions --- // protected void onOpen() { Map loaders = FileLoaderService.getInstance().getMapProviders(); StringBuilder sb = new StringBuilder(); ArrayList extensions = new ArrayList<>(loaders.keySet()); extensions.sort(null); for (String extension : extensions) { sb.append("*.").append(extension).append(", "); } sb.setLength(sb.length()-2); String description = sb.toString(); String[] array = extensions.toArray(new String[0]); JFileChooser chooser = new JFileChooser(); chooser.removeChoosableFileFilter(chooser.getFileFilter()); chooser.addChoosableFileFilter(new FileNameExtensionFilter("All files (" + description + ")", array)); for (String extension : extensions) { FileLoader loader = loaders.get(extension); chooser.addChoosableFileFilter(new FileNameExtensionFilter(loader.getDescription(), loader.getExtensions())); } chooser.setCurrentDirectory(configuration.getRecentLoadDirectory()); if (chooser.showOpenDialog(mainView.getMainFrame()) == JFileChooser.APPROVE_OPTION) { configuration.setRecentLoadDirectory(chooser.getCurrentDirectory()); openFile(chooser.getSelectedFile()); } } protected void onClose() { mainView.closeCurrentTab(); } protected void onSaveSource() { if (currentPage instanceof ContentSavable) { JFileChooser chooser = new JFileChooser(); JFrame mainFrame = mainView.getMainFrame(); chooser.setSelectedFile(new File(configuration.getRecentSaveDirectory(), ((ContentSavable)currentPage).getFileName())); if (chooser.showSaveDialog(mainFrame) == JFileChooser.APPROVE_OPTION) { File selectedFile = chooser.getSelectedFile(); configuration.setRecentSaveDirectory(chooser.getCurrentDirectory()); if (selectedFile.exists()) { String title = "Are you sure?"; String message = "The file '" + selectedFile.getAbsolutePath() + "' already isContainsIn.\n Do you want to replace the existing file?"; if (JOptionPane.showConfirmDialog(mainFrame, message, title, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { save(selectedFile); } } else { save(selectedFile); } } } } protected void save(File selectedFile) { try (OutputStream os = new FileOutputStream(selectedFile)) { ((ContentSavable)currentPage).save(this, os); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } protected void onSaveAllSources() { if (! saveAllSourcesController.isActivated()) { JComponent currentPanel = mainView.getSelectedMainPanel(); if (currentPanel instanceof SourcesSavable) { SourcesSavable sourcesSavable = (SourcesSavable)currentPanel; JFileChooser chooser = new JFileChooser(); JFrame mainFrame = mainView.getMainFrame(); chooser.setSelectedFile(new File(configuration.getRecentSaveDirectory(), sourcesSavable.getSourceFileName())); if (chooser.showSaveDialog(mainFrame) == JFileChooser.APPROVE_OPTION) { File selectedFile = chooser.getSelectedFile(); configuration.setRecentSaveDirectory(chooser.getCurrentDirectory()); if (selectedFile.exists()) { String title = "Are you sure?"; String message = "The file '" + selectedFile.getAbsolutePath() + "' already isContainsIn.\n Do you want to replace the existing file?"; if (JOptionPane.showConfirmDialog(mainFrame, message, title, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { saveAllSourcesController.show(executor, sourcesSavable, selectedFile); } } else { saveAllSourcesController.show(executor, sourcesSavable, selectedFile); } } } } } protected void onCopy() { if (currentPage instanceof ContentCopyable) { ((ContentCopyable)currentPage).copy(); } } protected void onPaste() { try { Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); if ((transferable != null) && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { Object obj = transferable.getTransferData(DataFlavor.stringFlavor); PasteHandler pasteHandler = PasteHandlerService.getInstance().get(obj); if (pasteHandler != null) { pasteHandler.paste(this, obj); } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } protected void onSelectAll() { if (currentPage instanceof ContentSelectable) { ((ContentSelectable)currentPage).selectAll(); } } protected void onFind() { if (currentPage instanceof ContentSearchable) { mainView.showFindPanel(); } } protected void onFindCriteriaChanged() { if (currentPage instanceof ContentSearchable) { mainView.setFindBackgroundColor(((ContentSearchable)currentPage).highlightText(mainView.getFindText(), mainView.getFindCaseSensitive())); } } protected void onFindNext() { if (currentPage instanceof ContentSearchable) { ((ContentSearchable)currentPage).findNext(mainView.getFindText(), mainView.getFindCaseSensitive()); } } protected void onOpenType() { openTypeController.show(getCollectionOfFutureIndexes(), uri -> openURI(uri)); } protected void onOpenTypeHierarchy() { if (currentPage instanceof FocusedTypeGettable) { FocusedTypeGettable ftg = (FocusedTypeGettable)currentPage; openTypeHierarchyController.show(getCollectionOfFutureIndexes(), ftg.getEntry(), ftg.getFocusedTypeName(), uri -> openURI(uri)); } } protected void onGoTo() { if (currentPage instanceof LineNumberNavigable) { LineNumberNavigable lnn = (LineNumberNavigable)currentPage; goToController.show(lnn, lineNumber -> lnn.goToLineNumber(lineNumber)); } } protected void onSearch() { searchInConstantPoolsController.show(getCollectionOfFutureIndexes(), uri -> openURI(uri)); } protected void onFindPrevious() { if (currentPage instanceof ContentSearchable) { ContentSearchable cs = (ContentSearchable)currentPage; cs.findPrevious(mainView.getFindText(), mainView.getFindCaseSensitive()); } } protected void onJdWebSite() { if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { try { desktop.browse(URI.create("http://java-decompiler.github.io")); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } } protected void onJdGuiIssues() { if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { try { desktop.browse(URI.create("https://github.com/java-decompiler/jd-gui/issues")); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } } protected void onJdCoreIssues() { if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { try { desktop.browse(URI.create("https://github.com/java-decompiler/jd-core/issues")); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } } @SuppressWarnings("unchecked") protected void onPreferences() { preferencesController.show(() -> { checkPreferencesChange(currentPage); mainView.preferencesChanged(getPreferences()); }); } protected void onAbout() { aboutController.show(); } protected void onCurrentPageChanged(JComponent page) { currentPage = page; checkPreferencesChange(page); checkIndexesChange(page); } protected void checkPreferencesChange(JComponent page) { if (page instanceof PreferencesChangeListener) { Map preferences = configuration.getPreferences(); Integer currentHashcode = Integer.valueOf(preferences.hashCode()); Integer lastHashcode = (Integer)page.getClientProperty("preferences-hashCode"); if (!currentHashcode.equals(lastHashcode)) { ((PreferencesChangeListener)page).preferencesChanged(preferences); page.putClientProperty("preferences-hashCode", currentHashcode); } } } protected void checkIndexesChange(JComponent page) { if (page instanceof IndexesChangeListener) { Collection> collectionOfFutureIndexes = getCollectionOfFutureIndexes(); Integer currentHashcode = Integer.valueOf(collectionOfFutureIndexes.hashCode()); Integer lastHashcode = (Integer)page.getClientProperty("collectionOfFutureIndexes-hashCode"); if (!currentHashcode.equals(lastHashcode)) { ((IndexesChangeListener)page).indexesChanged(collectionOfFutureIndexes); page.putClientProperty("collectionOfFutureIndexes-hashCode", currentHashcode); } } } // --- Operations --- // public void openFile(File file) { openFiles(Collections.singletonList(file)); } @SuppressWarnings("unchecked") public void openFiles(List files) { ArrayList errors = new ArrayList<>(); for (File file : files) { // Check input file if (file.exists()) { FileLoader loader = getFileLoader(file); if ((loader != null) && !loader.accept(this, file)) { errors.add("Invalid input fileloader: '" + file.getAbsolutePath() + "'"); } } else { errors.add("File not found: '" + file.getAbsolutePath() + "'"); } } if (errors.isEmpty()) { for (File file : files) { if (openURI(file.toURI())) { configuration.addRecentFile(file); mainView.updateRecentFilesMenu(configuration.getRecentFiles()); } } } else { StringBuilder messages = new StringBuilder(); int index = 0; for (String error : errors) { if (index > 0) { messages.append('\n'); } if (index >= 20) { messages.append("..."); break; } messages.append(error); index++; } JOptionPane.showMessageDialog(mainView.getMainFrame(), messages.toString(), "Error", JOptionPane.ERROR_MESSAGE); } } // --- Drop files transfer handler --- // protected class FilesTransferHandler extends TransferHandler { @Override public boolean canImport(TransferHandler.TransferSupport info) { return info.isDataFlavorSupported(DataFlavor.javaFileListFlavor); } @Override @SuppressWarnings("unchecked") public boolean importData(TransferHandler.TransferSupport info) { if (info.isDrop() && info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { try { openFiles((List)info.getTransferable().getTransferData(DataFlavor.javaFileListFlavor)); return true; } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } return false; } } // --- ComponentListener --- // protected class MainFrameListener extends ComponentAdapter { protected Configuration configuration; public MainFrameListener(Configuration configuration) { this.configuration = configuration; } @Override public void componentMoved(ComponentEvent e) { JFrame mainFrame = mainView.getMainFrame(); if ((mainFrame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { configuration.setMainWindowMaximize(true); } else { configuration.setMainWindowLocation(mainFrame.getLocation()); configuration.setMainWindowMaximize(false); } } @Override public void componentResized(ComponentEvent e) { JFrame mainFrame = mainView.getMainFrame(); if ((mainFrame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { configuration.setMainWindowMaximize(true); } else { configuration.setMainWindowSize(mainFrame.getSize()); configuration.setMainWindowMaximize(false); } } } protected void panelClosed() { SwingUtil.invokeLater(() -> { // Fire 'indexesChanged' event Collection> collectionOfFutureIndexes = getCollectionOfFutureIndexes(); for (IndexesChangeListener listener : containerChangeListeners) { listener.indexesChanged(collectionOfFutureIndexes); } if (currentPage instanceof IndexesChangeListener) { ((IndexesChangeListener)currentPage).indexesChanged(collectionOfFutureIndexes); } }); } // --- API --- // @Override @SuppressWarnings("unchecked") public boolean openURI(URI uri) { if (uri != null) { boolean success = mainView.openUri(uri); if (success == false) { UriLoader uriLoader = getUriLoader(uri); if (uriLoader != null) { success = uriLoader.load(this, uri); } } if (success) { addURI(uri); } return success; } return false; } @Override public boolean openURI(int x, int y, Collection entries, String query, String fragment) { if (entries != null) { if (entries.size() == 1) { // Open the single entry uri Container.Entry entry = entries.iterator().next(); return openURI(UriUtil.createURI(this, getCollectionOfFutureIndexes(), entry, query, fragment)); } else { // Multiple entries -> Open a "Select location" popup Collection> collectionOfFutureIndexes = getCollectionOfFutureIndexes(); selectLocationController.show( new Point(x+(16+2), y+2), entries, entry -> openURI(UriUtil.createURI(this, collectionOfFutureIndexes, entry, query, fragment)), // entry selected closure () -> {}); // popup close closure return true; } } return false; } @Override public void addURI(URI uri) { history.add(uri); SwingUtil.invokeLater(() -> { mainView.updateHistoryActions(); }); } @Override @SuppressWarnings("unchecked") public void addPanel(String title, Icon icon, String tip, T component) { mainView.addMainPanel(title, icon, tip, component); if (component instanceof ContentIndexable) { Future futureIndexes = executor.submit(() -> { Indexes indexes = ((ContentIndexable)component).index(this); SwingUtil.invokeLater(() -> { // Fire 'indexesChanged' event Collection> collectionOfFutureIndexes = getCollectionOfFutureIndexes(); for (IndexesChangeListener listener : containerChangeListeners) { listener.indexesChanged(collectionOfFutureIndexes); } if (currentPage instanceof IndexesChangeListener) { ((IndexesChangeListener) currentPage).indexesChanged(collectionOfFutureIndexes); } }); return indexes; }); component.putClientProperty("indexes", futureIndexes); } } @Override public Collection getContextualActions(Container.Entry entry, String fragment) { return ContextualActionsFactoryService.getInstance().get(this, entry, fragment); } @Override public FileLoader getFileLoader(File file) { return FileLoaderService.getInstance().get(this, file); } @Override public UriLoader getUriLoader(URI uri) { return UriLoaderService.getInstance().get(this, uri); } @Override public PanelFactory getMainPanelFactory(Container container) { return PanelFactoryService.getInstance().get(container); } @Override public ContainerFactory getContainerFactory(Path rootPath) { return ContainerFactoryService.getInstance().get(this, rootPath); } @Override public TreeNodeFactory getTreeNodeFactory(Container.Entry entry) { return TreeNodeFactoryService.getInstance().get(entry); } @Override public TypeFactory getTypeFactory(Container.Entry entry) { return TypeFactoryService.getInstance().get(entry); } @Override public Indexer getIndexer(Container.Entry entry) { return IndexerService.getInstance().get(entry); } @Override public SourceSaver getSourceSaver(Container.Entry entry) { return SourceSaverService.getInstance().get(entry); } @Override public Map getPreferences() { return configuration.getPreferences(); } @Override @SuppressWarnings("unchecked") public Collection> getCollectionOfFutureIndexes() { List mainPanels = mainView.getMainPanels(); ArrayList> list = new ArrayList>(mainPanels.size()) { @Override public int hashCode() { int hashCode = 1; try { for (Future futureIndexes : this) { hashCode *= 31; if (futureIndexes.isDone()) { hashCode += futureIndexes.get().hashCode(); } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } return hashCode; } }; for (JComponent panel : mainPanels) { Future futureIndexes = (Future)panel.getClientProperty("indexes"); if (futureIndexes != null) { list.add(futureIndexes); } } return list; } @Override public String getSource(Container.Entry entry) { return sourceLoaderService.getSource(this, entry); } @Override public void loadSource(Container.Entry entry, LoadSourceListener listener) { executor.execute(() -> { String source = sourceLoaderService.loadSource(this, entry); if ((source != null) && !source.isEmpty()) { listener.sourceLoaded(source); } }); } @Override public File loadSourceFile(Container.Entry entry) { return sourceLoaderService.getSourceFile(this, entry); } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/OpenTypeController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.net.UriUtil; import org.jd.gui.view.OpenTypeView; import javax.swing.*; import java.awt.*; import java.net.URI; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; import java.util.regex.Pattern; public class OpenTypeController implements IndexesChangeListener { protected static final int CACHE_MAX_ENTRIES = 5*20; protected API api; protected ScheduledExecutorService executor; protected Collection> collectionOfFutureIndexes; protected Consumer openCallback; protected JFrame mainFrame; protected OpenTypeView openTypeView; protected SelectLocationController selectLocationController; protected long indexesHashCode = 0L; protected Map> cache; public OpenTypeController(API api, ScheduledExecutorService executor, JFrame mainFrame) { this.api = api; this.executor = executor; this.mainFrame = mainFrame; // Create UI openTypeView = new OpenTypeView(api, mainFrame, this::updateList, this::onTypeSelected); selectLocationController = new SelectLocationController(api, mainFrame); // Create result cache cache = new LinkedHashMap>(CACHE_MAX_ENTRIES*3/2, 0.7f, true) { @Override protected boolean removeEldestEntry(Map.Entry> eldest) { return size() > CACHE_MAX_ENTRIES; } }; } public void show(Collection> collectionOfFutureIndexes, Consumer openCallback) { // Init attributes this.collectionOfFutureIndexes = collectionOfFutureIndexes; this.openCallback = openCallback; // Refresh view long hashCode = collectionOfFutureIndexes.hashCode(); if (hashCode != indexesHashCode) { // List of indexes has changed -> Refresh result list updateList(openTypeView.getPattern()); indexesHashCode = hashCode; } // Show openTypeView.show(); } @SuppressWarnings("unchecked") protected void updateList(String pattern) { int patternLength = pattern.length(); if (patternLength == 0) { // Display openTypeView.updateList(Collections.emptyMap()); } else { executor.execute(() -> { // Waiting the end of indexation... openTypeView.showWaitCursor(); Pattern regExpPattern = createRegExpPattern(pattern); Map> result = new HashMap<>(); try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Indexes indexes = futureIndexes.get(); String key = String.valueOf(indexes.hashCode()) + "***" + pattern; Map matchingEntries = cache.get(key); if (matchingEntries != null) { // Merge 'result' and 'matchingEntries' for (Map.Entry mapEntry : matchingEntries.entrySet()) { Collection collection = result.get(mapEntry.getKey()); if (collection == null) { result.put(mapEntry.getKey(), collection = new HashSet<>()); } collection.addAll(mapEntry.getValue()); } } else { // Waiting the end of indexation... Map index = indexes.getIndex("typeDeclarations"); if ((index != null) && !index.isEmpty()) { matchingEntries = new HashMap<>(); // Filter if (patternLength == 1) { match(pattern.charAt(0), index, matchingEntries); } else { String lastKey = key.substring(0, patternLength - 1); Map lastResult = cache.get(lastKey); if (lastResult != null) { match(regExpPattern, lastResult, matchingEntries); } else { match(regExpPattern, index, matchingEntries); } } // Store 'matchingEntries' cache.put(key, matchingEntries); // Merge 'result' and 'matchingEntries' for (Map.Entry mapEntry : matchingEntries.entrySet()) { Collection collection = result.get(mapEntry.getKey()); if (collection == null) { result.put(mapEntry.getKey(), collection = new HashSet<>()); } collection.addAll(mapEntry.getValue()); } } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } SwingUtilities.invokeLater(() -> { openTypeView.hideWaitCursor(); // Display openTypeView.updateList(result); }); }); } } @SuppressWarnings("unchecked") protected static void match(char c, Map index, Map result) { // Filter if (Character.isLowerCase(c)) { char upperCase = Character.toUpperCase(c); for (Map.Entry mapEntry : index.entrySet()) { String typeName = mapEntry.getKey(); Collection entries = mapEntry.getValue(); // Search last package separator int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); if (lastIndex < typeName.length()) { char first = typeName.charAt(lastIndex); if ((first == c) || (first == upperCase)) { add(result, typeName, entries); } } } } else { for (Map.Entry mapEntry : index.entrySet()) { String typeName = mapEntry.getKey(); Collection entries = mapEntry.getValue(); // Search last package separator int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); if ((lastIndex < typeName.length()) && (typeName.charAt(lastIndex) == c)) { add(result, typeName, entries); } } } } /** * Create a regular expression to match package, type and inner type name. * * Rules: * '*' matches 0 ou N characters * '?' matches 1 character * lower case matches insensitive case * upper case matches upper case */ protected static Pattern createRegExpPattern(String pattern) { // Create regular expression int patternLength = pattern.length(); StringBuilder sbPattern = new StringBuilder(patternLength * 4); for (int i=0; i 1) { sbPattern.append(".*"); } sbPattern.append(c); } else if (Character.isLowerCase(c)) { sbPattern.append('[').append(c).append(Character.toUpperCase(c)).append(']'); } else if (c == '*') { sbPattern.append(".*"); } else if (c == '?') { sbPattern.append("."); } else { sbPattern.append(c); } } sbPattern.append(".*"); return Pattern.compile(sbPattern.toString()); } @SuppressWarnings("unchecked") protected static void match(Pattern regExpPattern, Map index, Map result) { for (Map.Entry mapEntry : index.entrySet()) { String typeName = mapEntry.getKey(); Collection entries = mapEntry.getValue(); // Search last package separator int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); if (regExpPattern.matcher(typeName.substring(lastIndex)).matches()) { add(result, typeName, entries); } } } @SuppressWarnings("unchecked") protected static void add(Map map, String key, Collection value) { Collection collection = map.get(key); if (collection == null) { map.put(key, collection = new HashSet<>()); } collection.addAll(value); } protected void onTypeSelected(Point leftBottom, Collection entries, String typeName) { if (entries.size() == 1) { // Open the single entry uri openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entries.iterator().next(), null, typeName)); } else { // Multiple entries -> Open a "Select location" popup selectLocationController.show( new Point(leftBottom.x+(16+2), leftBottom.y+2), entries, (entry) -> openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entry, null, typeName)), // entry selected callback () -> openTypeView.focus()); // popup close callback } } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { if (openTypeView.isVisible()) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // And refresh updateList(openTypeView.getPattern()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.net.UriUtil; import org.jd.gui.view.OpenTypeHierarchyView; import javax.swing.*; import java.awt.*; import java.net.URI; import java.util.Collection; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; public class OpenTypeHierarchyController implements IndexesChangeListener { protected API api; private ScheduledExecutorService executor; protected JFrame mainFrame; protected OpenTypeHierarchyView openTypeHierarchyView; protected SelectLocationController selectLocationController; protected Collection> collectionOfFutureIndexes; protected Consumer openCallback; public OpenTypeHierarchyController(API api, ScheduledExecutorService executor, JFrame mainFrame) { this.api = api; this.executor = executor; this.mainFrame = mainFrame; // Create UI openTypeHierarchyView = new OpenTypeHierarchyView(api, mainFrame, this::onTypeSelected); selectLocationController = new SelectLocationController(api, mainFrame); } public void show(Collection> collectionOfFutureIndexes, Container.Entry entry, String typeName, Consumer openCallback) { // Init attributes this.collectionOfFutureIndexes = collectionOfFutureIndexes; this.openCallback = openCallback; executor.execute(() -> { // Waiting the end of indexation... openTypeHierarchyView.showWaitCursor(); SwingUtilities.invokeLater(() -> { openTypeHierarchyView.hideWaitCursor(); // Show openTypeHierarchyView.show(collectionOfFutureIndexes, entry, typeName); }); }); } protected void onTypeSelected(Point leftBottom, Collection entries, String typeName) { if (entries.size() == 1) { // Open the single entry uri openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entries.iterator().next(), null, typeName)); } else { // Multiple entries -> Open a "Select location" popup selectLocationController.show( new Point(leftBottom.x+(16+2), leftBottom.y+2), entries, (entry) -> openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entry, null, typeName)), // entry selected () -> openTypeHierarchyView.focus()); // popup closeClosure } } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { if (openTypeHierarchyView.isVisible()) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // And refresh openTypeHierarchyView.updateTree(collectionOfFutureIndexes); } } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/PreferencesController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.spi.PreferencesPanel; import org.jd.gui.view.PreferencesView; import javax.swing.*; import java.util.Collection; public class PreferencesController { protected PreferencesView preferencesView; public PreferencesController(Configuration configuration, JFrame mainFrame, Collection panels) { // Create UI preferencesView = new PreferencesView(configuration, mainFrame, panels); } public void show(Runnable okCallback) { // Show preferencesView.show(okCallback); } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.feature.SourcesSavable; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.SaveAllSourcesView; import javax.swing.*; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.ScheduledExecutorService; public class SaveAllSourcesController implements SourcesSavable.Controller, SourcesSavable.Listener { protected API api; protected SaveAllSourcesView saveAllSourcesView; protected boolean cancel; protected int counter; protected int mask; public SaveAllSourcesController(API api, JFrame mainFrame) { this.api = api; // Create UI this.saveAllSourcesView = new SaveAllSourcesView(mainFrame, this::onCanceled); } public void show(ScheduledExecutorService executor, SourcesSavable savable, File file) { // Show this.saveAllSourcesView.show(file); // Execute background task executor.execute(() -> { int fileCount = savable.getFileCount(); saveAllSourcesView.updateProgressBar(0); saveAllSourcesView.setMaxValue(fileCount); cancel = false; counter = 0; mask = 2; while (fileCount > 64) { fileCount >>= 1; mask <<= 1; } mask--; try { Path path = Paths.get(file.toURI()); Files.deleteIfExists(path); try { savable.save(api, this, this, path); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); saveAllSourcesView.showActionFailedDialog(); cancel = true; } if (cancel) { Files.deleteIfExists(path); } } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); } saveAllSourcesView.hide(); }); } public boolean isActivated() { return saveAllSourcesView.isVisible(); } protected void onCanceled() { cancel = true; } // --- SourcesSavable.Controller --- // @Override public boolean isCancelled() { return cancel; } // --- SourcesSavable.Listener --- // @Override public void pathSaved(Path path) { if (((counter++) & mask) == 0) { saveAllSourcesView.updateProgressBar(counter); } } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.api.model.Type; import org.jd.gui.model.container.DelegatingFilterContainer; import org.jd.gui.service.type.TypeFactoryService; import org.jd.gui.spi.TypeFactory; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.function.TriConsumer; import org.jd.gui.view.SearchInConstantPoolsView; import javax.swing.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.regex.Pattern; public class SearchInConstantPoolsController implements IndexesChangeListener { protected static final int CACHE_MAX_ENTRIES = 5*20*9; protected API api; protected ScheduledExecutorService executor; protected JFrame mainFrame; protected SearchInConstantPoolsView searchInConstantPoolsView; protected Map> cache; protected Set delegatingFilterContainers = new HashSet<>(); protected Collection> collectionOfFutureIndexes; protected Consumer openCallback; protected long indexesHashCode = 0L; @SuppressWarnings("unchecked") public SearchInConstantPoolsController(API api, ScheduledExecutorService executor, JFrame mainFrame) { this.api = api; this.executor = executor; this.mainFrame = mainFrame; // Create UI this.searchInConstantPoolsView = new SearchInConstantPoolsView( api, mainFrame, new BiConsumer() { @Override public void accept(String pattern, Integer flags) { updateTree(pattern, flags); } }, new TriConsumer() { @Override public void accept(URI uri, String pattern, Integer flags) { onTypeSelected(uri, pattern, flags); } } ); // Create result cache this.cache = new LinkedHashMap>(CACHE_MAX_ENTRIES*3/2, 0.7f, true) { @Override protected boolean removeEldestEntry(Map.Entry> eldest) { return size() > CACHE_MAX_ENTRIES; } }; } public void show(Collection> collectionOfFutureIndexes, Consumer openCallback) { // Init attributes this.collectionOfFutureIndexes = collectionOfFutureIndexes; this.openCallback = openCallback; // Refresh view long hashCode = collectionOfFutureIndexes.hashCode(); if (hashCode != indexesHashCode) { // List of indexes has changed updateTree(searchInConstantPoolsView.getPattern(), searchInConstantPoolsView.getFlags()); indexesHashCode = hashCode; } // Show searchInConstantPoolsView.show(); } @SuppressWarnings("unchecked") protected void updateTree(String pattern, int flags) { delegatingFilterContainers.clear(); executor.execute(() -> { // Waiting the end of indexation... searchInConstantPoolsView.showWaitCursor(); int matchingTypeCount = 0; int patternLength = pattern.length(); if (patternLength > 0) { try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Indexes indexes = futureIndexes.get(); HashSet matchingEntries = new HashSet<>(); // Find matched entries filter(indexes, pattern, flags, matchingEntries); if (!matchingEntries.isEmpty()) { // Search root container with first matching entry Container.Entry parentEntry = matchingEntries.iterator().next(); Container container = null; while (parentEntry.getContainer().getRoot() != null) { container = parentEntry.getContainer(); parentEntry = container.getRoot().getParent(); } // TODO In a future release, display matching strings, types, inner-types, fields and methods, not only matching files matchingEntries = getOuterEntries(matchingEntries); matchingTypeCount += matchingEntries.size(); // Create a filtered container delegatingFilterContainers.add(new DelegatingFilterContainer(container, matchingEntries)); } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } final int count = matchingTypeCount; searchInConstantPoolsView.hideWaitCursor(); searchInConstantPoolsView.updateTree(delegatingFilterContainers, count); }); } protected HashSet getOuterEntries(Set matchingEntries) { HashMap innerTypeEntryToOuterTypeEntry = new HashMap<>(); HashSet matchingOuterEntriesSet = new HashSet<>(); for (Container.Entry entry : matchingEntries) { TypeFactory typeFactory = TypeFactoryService.getInstance().get(entry); if (typeFactory != null) { Type type = typeFactory.make(api, entry, null); if ((type != null) && (type.getOuterName() != null)) { Container.Entry outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry); if (outerTypeEntry == null) { HashMap typeNameToEntry = new HashMap<>(); HashMap innerTypeNameToOuterTypeName = new HashMap<>(); // Populate "typeNameToEntry" and "innerTypeNameToOuterTypeName" for (Container.Entry e : entry.getParent().getChildren()) { typeFactory = TypeFactoryService.getInstance().get(e); if (typeFactory != null) { type = typeFactory.make(api, e, null); if (type != null) { typeNameToEntry.put(type.getName(), e); if (type.getOuterName() != null) { innerTypeNameToOuterTypeName.put(type.getName(), type.getOuterName()); } } } } // Search outer type entries and populate "innerTypeEntryToOuterTypeEntry" for (Map.Entry e : innerTypeNameToOuterTypeName.entrySet()) { Container.Entry innerTypeEntry = typeNameToEntry.get(e.getKey()); if (innerTypeEntry != null) { String outerTypeName = e.getValue(); for (;;) { String typeName = innerTypeNameToOuterTypeName.get(outerTypeName); if (typeName != null) { outerTypeName = typeName; } else { break; } } outerTypeEntry = typeNameToEntry.get(outerTypeName); if (outerTypeEntry != null) { innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry); } } } // Get outer type entry outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry); if (outerTypeEntry == null) { outerTypeEntry = entry; } } matchingOuterEntriesSet.add(outerTypeEntry); } else{ matchingOuterEntriesSet.add(entry); } } } return matchingOuterEntriesSet; } protected void filter(Indexes indexes, String pattern, int flags, Set matchingEntries) { boolean declarations = ((flags & SearchInConstantPoolsView.SEARCH_DECLARATION) != 0); boolean references = ((flags & SearchInConstantPoolsView.SEARCH_REFERENCE) != 0); if ((flags & SearchInConstantPoolsView.SEARCH_TYPE) != 0) { if (declarations) match(indexes, "typeDeclarations", pattern, SearchInConstantPoolsController::matchTypeEntriesWithChar, SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries); if (references) match(indexes, "typeReferences", pattern, SearchInConstantPoolsController::matchTypeEntriesWithChar, SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries); } if ((flags & SearchInConstantPoolsView.SEARCH_CONSTRUCTOR) != 0) { if (declarations) match(indexes, "constructorDeclarations", pattern, SearchInConstantPoolsController::matchTypeEntriesWithChar, SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries); if (references) match(indexes, "constructorReferences", pattern, SearchInConstantPoolsController::matchTypeEntriesWithChar, SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries); } if ((flags & SearchInConstantPoolsView.SEARCH_METHOD) != 0) { if (declarations) match(indexes, "methodDeclarations", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); if (references) match(indexes, "methodReferences", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); } if ((flags & SearchInConstantPoolsView.SEARCH_FIELD) != 0) { if (declarations) match(indexes, "fieldDeclarations", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); if (references) match(indexes, "fieldReferences", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); } if ((flags & SearchInConstantPoolsView.SEARCH_STRING) != 0) { if (declarations || references) match(indexes, "strings", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); } if ((flags & SearchInConstantPoolsView.SEARCH_MODULE) != 0) { if (declarations) match(indexes, "javaModuleDeclarations", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); if (references) match(indexes, "javaModuleReferences", pattern, SearchInConstantPoolsController::matchWithChar, SearchInConstantPoolsController::matchWithString, matchingEntries); } } @SuppressWarnings("unchecked") protected void match(Indexes indexes, String indexName, String pattern, BiFunction, Map> matchWithCharFunction, BiFunction, Map> matchWithStringFunction, Set matchingEntries) { int patternLength = pattern.length(); if (patternLength > 0) { String key = String.valueOf(indexes.hashCode()) + "***" + indexName + "***" + pattern; Map matchedEntries = cache.get(key); if (matchedEntries == null) { Map index = indexes.getIndex(indexName); if (index != null) { if (patternLength == 1) { matchedEntries = matchWithCharFunction.apply(pattern.charAt(0), index); } else { String lastKey = key.substring(0, key.length() - 1); Map lastMatchedTypes = cache.get(lastKey); if (lastMatchedTypes != null) { matchedEntries = matchWithStringFunction.apply(pattern, lastMatchedTypes); } else { matchedEntries = matchWithStringFunction.apply(pattern, index); } } } // Cache matchingEntries cache.put(key, matchedEntries); } if (matchedEntries != null) { for (Collection entries : matchedEntries.values()) { matchingEntries.addAll(entries); } } } } protected static Map matchTypeEntriesWithChar(char c, Map index) { if ((c == '*') || (c == '?')) { return index; } else { Map map = new HashMap<>(); for (String typeName : index.keySet()) { // Search last package separator int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); if ((lastIndex < typeName.length()) && (typeName.charAt(lastIndex) == c)) { map.put(typeName, index.get(typeName)); } } return map; } } protected static Map matchTypeEntriesWithString(String pattern, Map index) { Pattern p = createPattern(pattern); Map map = new HashMap<>(); for (String typeName : index.keySet()) { // Search last package separator int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); if (p.matcher(typeName.substring(lastIndex)).matches()) { map.put(typeName, index.get(typeName)); } } return map; } protected static Map matchWithChar(char c, Map index) { if ((c == '*') || (c == '?')) { return index; } else { Map map = new HashMap<>(); for (String key : index.keySet()) { if (!key.isEmpty() && (key.charAt(0) == c)) { map.put(key, index.get(key)); } } return map; } } protected static Map matchWithString(String pattern, Map index) { Pattern p = createPattern(pattern); Map map = new HashMap<>(); for (String key : index.keySet()) { if (p.matcher(key).matches()) { map.put(key, index.get(key)); } } return map; } /** * Create a simple regular expression * * Rules: * '*' matchTypeEntries 0 ou N characters * '?' matchTypeEntries 1 character */ protected static Pattern createPattern(String pattern) { int patternLength = pattern.length(); StringBuilder sbPattern = new StringBuilder(patternLength * 2); for (int i = 0; i < patternLength; i++) { char c = pattern.charAt(i); if (c == '*') { sbPattern.append(".*"); } else if (c == '?') { sbPattern.append('.'); } else if (c == '.') { sbPattern.append("\\."); } else { sbPattern.append(c); } } sbPattern.append(".*"); return Pattern.compile(sbPattern.toString()); } protected void onTypeSelected(URI uri, String pattern, int flags) { // Open the single entry uri Container.Entry entry = null; for (DelegatingFilterContainer container : delegatingFilterContainers) { entry = container.getEntry(uri); if (entry != null) break; } if (entry != null) { StringBuilder sbPattern = new StringBuilder(200 + pattern.length()); sbPattern.append("highlightPattern="); sbPattern.append(pattern); sbPattern.append("&highlightFlags="); if ((flags & SearchInConstantPoolsView.SEARCH_DECLARATION) != 0) sbPattern.append('d'); if ((flags & SearchInConstantPoolsView.SEARCH_REFERENCE) != 0) sbPattern.append('r'); if ((flags & SearchInConstantPoolsView.SEARCH_TYPE) != 0) sbPattern.append('t'); if ((flags & SearchInConstantPoolsView.SEARCH_CONSTRUCTOR) != 0) sbPattern.append('c'); if ((flags & SearchInConstantPoolsView.SEARCH_METHOD) != 0) sbPattern.append('m'); if ((flags & SearchInConstantPoolsView.SEARCH_FIELD) != 0) sbPattern.append('f'); if ((flags & SearchInConstantPoolsView.SEARCH_STRING) != 0) sbPattern.append('s'); if ((flags & SearchInConstantPoolsView.SEARCH_MODULE) != 0) sbPattern.append('M'); // TODO In a future release, add 'highlightScope' to display search results in correct type and inner-type // def type = TypeFactoryService.instance.get(entry)?.make(api, entry, null) // if (type) { // sbPattern.append('&highlightScope=') // sbPattern.append(type.name) // // def query = sbPattern.toString() // def outerPath = UriUtil.getOuterPath(collectionOfFutureIndexes, entry, type) // // openClosure(new URI(entry.uri.scheme, entry.uri.host, outerPath, query, null)) // } else { String query = sbPattern.toString(); URI u = entry.getUri(); try { openCallback.accept(new URI(u.getScheme(), u.getHost(), u.getPath(), query, null)); } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } // } } } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { if (searchInConstantPoolsView.isVisible()) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // And refresh updateTree(searchInConstantPoolsView.getPattern(), searchInConstantPoolsView.getFlags()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/controller/SelectLocationController.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.controller; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.model.container.DelegatingFilterContainer; import org.jd.gui.service.type.TypeFactoryService; import org.jd.gui.spi.TypeFactory; import org.jd.gui.view.SelectLocationView; import javax.swing.*; import java.awt.*; import java.net.URI; import java.util.*; import java.util.function.Consumer; public class SelectLocationController { protected static final ContainerEntryComparator CONTAINER_ENTRY_COMPARATOR = new ContainerEntryComparator(); protected API api; protected SelectLocationView selectLocationView; public SelectLocationController(API api, JFrame mainFrame) { this.api = api; // Create UI selectLocationView = new SelectLocationView(api, mainFrame); } @SuppressWarnings("unchecked") public void show(Point location, Collection entries, Consumer selectedLocationCallback, Runnable closeCallback) { // Show UI HashMap> map = new HashMap<>(); for (Container.Entry entry : entries) { Container container = entry.getContainer(); // Search root container while (true) { Container parentContainer = container.getRoot().getParent().getContainer(); if (parentContainer.getRoot() == null) { break; } else { container = parentContainer; } } ArrayList list = map.get(container); if (list == null) { map.put(container, list=new ArrayList<>()); } list.add(entry); } HashSet delegatingFilterContainers = new HashSet<>(); for (Map.Entry> mapEntry : map.entrySet()) { Container container = mapEntry.getKey(); // Create a filtered container // TODO In a future release, display matching types and inner-types, not only matching files delegatingFilterContainers.add(new DelegatingFilterContainer(container, getOuterEntries(mapEntry.getValue()))); } Consumer selectedEntryCallback = uri -> onLocationSelected(delegatingFilterContainers, uri, selectedLocationCallback); selectLocationView.show(location, delegatingFilterContainers, entries.size(), selectedEntryCallback, closeCallback); } protected Collection getOuterEntries(Collection entries) { HashMap innerTypeEntryToOuterTypeEntry = new HashMap<>(); HashSet outerEntriesSet = new HashSet<>(); for (Container.Entry entry : entries) { Container.Entry outerTypeEntry = null; TypeFactory factory = TypeFactoryService.getInstance().get(entry); if (factory != null) { Type type = factory.make(api, entry, null); if ((type != null) && (type.getOuterName() != null)) { outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry); if (outerTypeEntry == null) { HashMap typeNameToEntry = new HashMap<>(); HashMap innerTypeNameToOuterTypeName = new HashMap<>(); // Populate "typeNameToEntry" and "innerTypeNameToOuterTypeName" for (Container.Entry e : entry.getParent().getChildren()) { factory = TypeFactoryService.getInstance().get(e); if (factory != null) { type = factory.make(api, e, null); if (type != null) { typeNameToEntry.put(type.getName(), e); if (type.getOuterName() != null) { innerTypeNameToOuterTypeName.put(type.getName(), type.getOuterName()); } } } } // Search outer type entries and populate "innerTypeEntryToOuterTypeEntry" for (Map.Entry e : innerTypeNameToOuterTypeName.entrySet()) { Container.Entry innerTypeEntry = typeNameToEntry.get(e.getKey()); if (innerTypeEntry != null) { String outerTypeName = e.getValue(); for (;;) { String typeName = innerTypeNameToOuterTypeName.get(outerTypeName); if (typeName != null) { outerTypeName = typeName; } else { break; } } outerTypeEntry = typeNameToEntry.get(outerTypeName); if (outerTypeEntry != null) { innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry); } } } // Get outer type entry outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry); } } } if (outerTypeEntry != null) { outerEntriesSet.add(outerTypeEntry); } else { outerEntriesSet.add(entry); } } // Return outer type entries sorted by path ArrayList result = new ArrayList<>(outerEntriesSet); result.sort(CONTAINER_ENTRY_COMPARATOR); return result; } protected void onLocationSelected(Set delegatingFilterContainers, URI uri, Consumer selectedLocationCallback) { // Open the single entry uri Container.Entry entry = null; for (DelegatingFilterContainer container : delegatingFilterContainers) { entry = container.getEntry(uri); if (entry != null) { break; } } if (entry != null) { selectedLocationCallback.accept(entry); } } protected static class ContainerEntryComparator implements Comparator { @Override public int compare(Container.Entry e1, Container.Entry e2) { return e1.getPath().compareTo(e2.getPath()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/model/configuration/Configuration.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.configuration; import org.jd.gui.Constants; import java.awt.*; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Configuration { protected Point mainWindowLocation; protected Dimension mainWindowSize; protected boolean mainWindowMaximize; protected String lookAndFeel; protected List recentFiles = new ArrayList<>(); protected File recentLoadDirectory; protected File recentSaveDirectory; protected Map preferences = new HashMap<>(); public Point getMainWindowLocation() { return mainWindowLocation; } public Dimension getMainWindowSize() { return mainWindowSize; } public boolean isMainWindowMaximize() { return mainWindowMaximize; } public String getLookAndFeel() { return lookAndFeel; } public List getRecentFiles() { return recentFiles; } public File getRecentLoadDirectory() { return recentLoadDirectory; } public File getRecentSaveDirectory() { return recentSaveDirectory; } public Map getPreferences() { return preferences; } public void setMainWindowLocation(Point mainWindowLocation) { this.mainWindowLocation = mainWindowLocation; } public void setMainWindowSize(Dimension mainWindowSize) { this.mainWindowSize = mainWindowSize; } public void setMainWindowMaximize(boolean mainWindowMaximize) { this.mainWindowMaximize = mainWindowMaximize; } public void setLookAndFeel(String lookAndFeel) { this.lookAndFeel = lookAndFeel; } public void setRecentFiles(List recentFiles) { this.recentFiles = recentFiles; } public void setRecentLoadDirectory(File recentLoadDirectory) { this.recentLoadDirectory = recentLoadDirectory; } public void setRecentSaveDirectory(File recentSaveDirectory) { this.recentSaveDirectory = recentSaveDirectory; } public void setPreferences(Map preferences) { this.preferences = preferences; } public void addRecentFile(File file) { recentFiles.remove(file); recentFiles.add(0, file); if (recentFiles.size() > Constants.MAX_RECENT_FILES) { recentFiles.remove(Constants.MAX_RECENT_FILES); } } } ================================================ FILE: app/src/main/java/org/jd/gui/model/container/DelegatingFilterContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.model.Container; import java.io.InputStream; import java.net.URI; import java.util.*; public class DelegatingFilterContainer implements Container { protected static final URI DEFAULT_ROOT_URI = URI.create("file:."); protected Container container; protected DelegatedEntry root; protected Set validEntries = new HashSet<>(); protected Map uriToDelegatedEntry = new HashMap<>(); protected Map uriToDelegatedContainer = new HashMap<>(); public DelegatingFilterContainer(Container container, Collection entries) { this.container = container; this.root = getDelegatedEntry(container.getRoot()); for (Entry entry : entries) { while ((entry != null) && !validEntries.contains(entry.getUri())) { validEntries.add(entry.getUri()); entry = entry.getParent(); } } } @Override public String getType() { return container.getType(); } @Override public Container.Entry getRoot() { return root; } public Container.Entry getEntry(URI uri) { return uriToDelegatedEntry.get(uri); } public Set getUris() { return validEntries; } protected DelegatedEntry getDelegatedEntry(Container.Entry entry) { URI uri = entry.getUri(); DelegatedEntry delegatedEntry = uriToDelegatedEntry.get(uri); if (delegatedEntry == null) { uriToDelegatedEntry.put(uri, delegatedEntry =new DelegatedEntry(entry)); } return delegatedEntry; } protected DelegatedContainer getDelegatedContainer(Container container) { Entry root = container.getRoot(); URI uri = (root == null) ? DEFAULT_ROOT_URI : root.getUri(); DelegatedContainer delegatedContainer = uriToDelegatedContainer.get(uri); if (delegatedContainer == null) { uriToDelegatedContainer.put(uri, delegatedContainer =new DelegatedContainer(container)); } return delegatedContainer; } protected class DelegatedEntry implements Entry, Comparable { protected Entry entry; protected Collection children; public DelegatedEntry(Entry entry) { this.entry = entry; } @Override public Container getContainer() { return getDelegatedContainer(entry.getContainer()); } @Override public Entry getParent() { return getDelegatedEntry(entry.getParent()); } @Override public URI getUri() { return entry.getUri(); } @Override public String getPath() { return entry.getPath(); } @Override public boolean isDirectory() { return entry.isDirectory(); } @Override public long length() { return entry.length(); } @Override public InputStream getInputStream() { return entry.getInputStream(); } @Override public Collection getChildren() { if (children == null) { children = new ArrayList<>(); for (Entry child : entry.getChildren()) { if (validEntries.contains(child.getUri())) { children.add(getDelegatedEntry(child)); } } } return children; } @Override public int compareTo(DelegatedEntry other) { if (entry.isDirectory()) { if (!other.isDirectory()) { return -1; } } else { if (other.isDirectory()) { return 1; } } return entry.getPath().compareTo(other.getPath()); } } protected class DelegatedContainer implements Container { protected Container container; public DelegatedContainer(Container container) { this.container = container; } @Override public String getType() { return container.getType(); } @Override public Entry getRoot() { return getDelegatedEntry(container.getRoot()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/model/history/History.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.history; import java.net.URI; import java.util.ArrayList; public class History { protected URI current = null; protected ArrayList backward = new ArrayList<>(); protected ArrayList forward = new ArrayList<>(); public void add(URI uri) { if (current == null) { // Init history forward.clear(); current = uri; return; } if (current.equals(uri)) { // Already stored -> Nothing to do return; } if (uri.getPath().toString().equals(current.getPath().toString())) { if ((uri.getFragment() == null) && (uri.getQuery() == null)) { // Ignore } else if ((current.getFragment() == null) && (current.getQuery() == null)) { // Replace current URI current = uri; } else { // Store URI forward.clear(); backward.add(current); current = uri; } return; } if (uri.toString().startsWith(current.toString())) { // Replace current URI current = uri; return; } if (current.toString().startsWith(uri.toString())) { // Parent URI -> Nothing to do return; } // Store URI forward.clear(); backward.add(current); current = uri; } public URI backward() { if (! backward.isEmpty()) { forward.add(current); int size = backward.size(); current = backward.remove(size-1); } return current; } public URI forward() { if (! forward.isEmpty()) { backward.add(current); int size = forward.size(); current = forward.remove(size-1); } return current; } public boolean canBackward() { return !backward.isEmpty(); } public boolean canForward() { return !forward.isEmpty(); } } ================================================ FILE: app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.actions; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.ContextualActionsFactory; import javax.swing.*; import java.util.*; public class ContextualActionsFactoryService { protected static final ContextualActionsFactoryService CONTEXTUAL_ACTIONS_FACTORY_SERVICE = new ContextualActionsFactoryService(); public static ContextualActionsFactoryService getInstance() { return CONTEXTUAL_ACTIONS_FACTORY_SERVICE; } protected static final ActionNameComparator COMPARATOR = new ActionNameComparator(); protected final Collection providers = ExtensionService.getInstance().load(ContextualActionsFactory.class); public Collection get(API api, Container.Entry entry, String fragment) { HashMap> mapActions = new HashMap<>(); for (ContextualActionsFactory provider : providers) { Collection actions = provider.make(api, entry, fragment); for (Action action : actions) { String groupName = (String)action.getValue(ContextualActionsFactory.GROUP_NAME); ArrayList list = mapActions.get(groupName); if (list == null) { mapActions.put(groupName, list=new ArrayList<>()); } list.add(action); } } if (!mapActions.isEmpty()) { ArrayList result = new ArrayList<>(); // Sort by group names ArrayList groupNames = new ArrayList<>(mapActions.keySet()); Collections.sort(groupNames); for (String groupName : groupNames) { if (! result.isEmpty()) { // Add 'null' to mark a separator result.add(null); } // Sort by names ArrayList actions = mapActions.get(groupName); Collections.sort(actions, COMPARATOR); result.addAll(actions); } return result; } else { return Collections.emptyList(); } } protected static class ActionNameComparator implements Comparator { @Override public int compare(Action a1, Action a2) { String n1 = (String)a1.getValue(Action.NAME); if (n1 == null) { n1 = ""; } String n2 = (String)a2.getValue(Action.NAME); if (n2 == null) { n2 = ""; } return n1.compareTo(n2); } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersister.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.configuration; import org.jd.gui.model.configuration.Configuration; public interface ConfigurationPersister { Configuration load(); void save(Configuration configuration); } ================================================ FILE: app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.configuration; public class ConfigurationPersisterService { protected static final ConfigurationPersisterService CONFIGURATION_PERSISTER_SERVICE = new ConfigurationPersisterService(); protected ConfigurationPersister provider = new ConfigurationXmlPersisterProvider(); public static ConfigurationPersisterService getInstance() { return CONFIGURATION_PERSISTER_SERVICE; } protected ConfigurationPersisterService() {} public ConfigurationPersister get() { return provider; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.configuration; import org.jd.gui.Constants; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.service.platform.PlatformService; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import javax.xml.stream.*; import java.awt.*; import java.io.*; import java.net.URL; import java.util.*; import java.util.List; import java.util.jar.Manifest; public class ConfigurationXmlPersisterProvider implements ConfigurationPersister { protected static final String ERROR_BACKGROUND_COLOR = "JdGuiPreferences.errorBackgroundColor"; protected static final String JD_CORE_VERSION = "JdGuiPreferences.jdCoreVersion"; protected static final File FILE = getConfigFile(); protected static File getConfigFile() { String configFilePath = System.getProperty(Constants.CONFIG_FILENAME); if (configFilePath != null) { File configFile = new File(configFilePath); if (configFile.exists()) { return configFile; } } if (PlatformService.getInstance().isLinux()) { // See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html String xdgConfigHome = System.getenv("XDG_CONFIG_HOME"); if (xdgConfigHome != null) { File xdgConfigHomeFile = new File(xdgConfigHome); if (xdgConfigHomeFile.exists()) { return new File(xdgConfigHomeFile, Constants.CONFIG_FILENAME); } } File userConfigFile = new File(System.getProperty("user.home"), ".config"); if (userConfigFile.exists()) { return new File(userConfigFile, Constants.CONFIG_FILENAME); } } else if (PlatformService.getInstance().isWindows()) { // See: http://blogs.msdn.com/b/patricka/archive/2010/03/18/where-should-i-store-my-data-and-configuration-files-if-i-target-multiple-os-versions.aspx String roamingConfigHome = System.getenv("APPDATA"); if (roamingConfigHome != null) { File roamingConfigHomeFile = new File(roamingConfigHome); if (roamingConfigHomeFile.exists()) { return new File(roamingConfigHomeFile, Constants.CONFIG_FILENAME); } } } return new File(Constants.CONFIG_FILENAME); } @Override public Configuration load() { // Default values Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int w = (screenSize.width>Constants.DEFAULT_WIDTH) ? Constants.DEFAULT_WIDTH : screenSize.width; int h = (screenSize.height>Constants.DEFAULT_HEIGHT) ? Constants.DEFAULT_HEIGHT : screenSize.height; int x = (screenSize.width-w)/2; int y = (screenSize.height-h)/2; Configuration config = new Configuration(); config.setMainWindowLocation(new Point(x, y)); config.setMainWindowSize(new Dimension(w, h)); config.setMainWindowMaximize(false); String defaultLaf = System.getProperty("swing.defaultlaf"); config.setLookAndFeel((defaultLaf != null) ? defaultLaf : UIManager.getSystemLookAndFeelClassName()); File recentSaveDirectory = new File(System.getProperty("user.dir")); config.setRecentLoadDirectory(recentSaveDirectory); config.setRecentSaveDirectory(recentSaveDirectory); if (FILE.exists()) { try (FileInputStream fis = new FileInputStream(FILE)) { XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(fis); // Load values String name = ""; Stack names = new Stack<>(); List recentFiles = new ArrayList<>(); boolean maximize = false; Map preferences = config.getPreferences(); while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: names.push(name); name += '/' + reader.getLocalName(); switch (name) { case "/configuration/gui/mainWindow/location": x = Integer.parseInt(reader.getAttributeValue(null, "x")); y = Integer.parseInt(reader.getAttributeValue(null, "y")); break; case "/configuration/gui/mainWindow/size": w = Integer.parseInt(reader.getAttributeValue(null, "w")); h = Integer.parseInt(reader.getAttributeValue(null, "h")); break; } break; case XMLStreamConstants.END_ELEMENT: name = names.pop(); break; case XMLStreamConstants.CHARACTERS: switch (name) { case "/configuration/recentFilePaths/filePath": File file = new File(reader.getText().trim()); if (file.exists()) { recentFiles.add(file); } break; case "/configuration/recentDirectories/loadPath": file = new File(reader.getText().trim()); if (file.exists()) { config.setRecentLoadDirectory(file); } break; case "/configuration/recentDirectories/savePath": file = new File(reader.getText().trim()); if (file.exists()) { config.setRecentSaveDirectory(file); } break; case "/configuration/gui/lookAndFeel": config.setLookAndFeel(reader.getText().trim()); break; case "/configuration/gui/mainWindow/maximize": maximize = Boolean.parseBoolean(reader.getText().trim()); break; default: if (name.startsWith("/configuration/preferences/")) { String key = name.substring("/configuration/preferences/".length()); preferences.put(key, reader.getText().trim()); } break; } break; } } if (recentFiles.size() > Constants.MAX_RECENT_FILES) { // Truncate recentFiles = recentFiles.subList(0, Constants.MAX_RECENT_FILES); } config.setRecentFiles(recentFiles); if ((x >= 0) && (y >= 0) && (x + w < screenSize.width) && (y + h < screenSize.height)) { // Update preferences config.setMainWindowLocation(new Point(x, y)); config.setMainWindowSize(new Dimension(w, h)); config.setMainWindowMaximize(maximize); } reader.close(); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } if (! config.getPreferences().containsKey(ERROR_BACKGROUND_COLOR)) { config.getPreferences().put(ERROR_BACKGROUND_COLOR, "0xFF6666"); } config.getPreferences().put(JD_CORE_VERSION, getJdCoreVersion()); return config; } protected String getJdCoreVersion() { try { Enumeration enumeration = ConfigurationXmlPersisterProvider.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); while (enumeration.hasMoreElements()) { try (InputStream is = enumeration.nextElement().openStream()) { String attribute = new Manifest(is).getMainAttributes().getValue("JD-Core-Version"); if (attribute != null) { return attribute; } } } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } return "SNAPSHOT"; } @Override public void save(Configuration configuration) { Point l = configuration.getMainWindowLocation(); Dimension s = configuration.getMainWindowSize(); try (FileOutputStream fos = new FileOutputStream(FILE)) { XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(fos); // Save values writer.writeStartDocument(); writer.writeCharacters("\n"); writer.writeStartElement("configuration"); writer.writeCharacters("\n\t"); writer.writeStartElement("gui"); writer.writeCharacters("\n\t\t"); writer.writeStartElement("mainWindow"); writer.writeCharacters("\n\t\t\t"); writer.writeStartElement("location"); writer.writeAttribute("x", String.valueOf(l.x)); writer.writeAttribute("y", String.valueOf(l.y)); writer.writeEndElement(); writer.writeCharacters("\n\t\t\t"); writer.writeStartElement("size"); writer.writeAttribute("w", String.valueOf(s.width)); writer.writeAttribute("h", String.valueOf(s.height)); writer.writeEndElement(); writer.writeCharacters("\n\t\t\t"); writer.writeStartElement("maximize"); writer.writeCharacters(String.valueOf(configuration.isMainWindowMaximize())); writer.writeEndElement(); writer.writeCharacters("\n\t\t"); writer.writeEndElement(); writer.writeCharacters("\n\t\t"); writer.writeStartElement("lookAndFeel"); writer.writeCharacters(configuration.getLookAndFeel()); writer.writeEndElement(); writer.writeCharacters("\n\t"); writer.writeEndElement(); writer.writeCharacters("\n\t"); writer.writeStartElement("recentFilePaths"); for (File recentFile : configuration.getRecentFiles()) { writer.writeCharacters("\n\t\t"); writer.writeStartElement("filePath"); writer.writeCharacters(recentFile.getAbsolutePath()); writer.writeEndElement(); } writer.writeCharacters("\n\t"); writer.writeEndElement(); writer.writeCharacters("\n\t"); writer.writeStartElement("recentDirectories"); writer.writeCharacters("\n\t\t"); writer.writeStartElement("loadPath"); writer.writeCharacters(configuration.getRecentLoadDirectory().getAbsolutePath()); writer.writeEndElement(); writer.writeCharacters("\n\t\t"); writer.writeStartElement("savePath"); writer.writeCharacters(configuration.getRecentSaveDirectory().getAbsolutePath()); writer.writeEndElement(); writer.writeCharacters("\n\t"); writer.writeEndElement(); writer.writeCharacters("\n\t"); writer.writeStartElement("preferences"); for (Map.Entry preference : configuration.getPreferences().entrySet()) { writer.writeCharacters("\n\t\t"); writer.writeStartElement(preference.getKey()); writer.writeCharacters(preference.getValue()); writer.writeEndElement(); } writer.writeCharacters("\n\t"); writer.writeEndElement(); writer.writeCharacters("\n"); writer.writeEndElement(); writer.writeEndDocument(); writer.close(); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.ContainerFactory; import java.nio.file.Path; import java.util.Collection; public class ContainerFactoryService { protected static final ContainerFactoryService CONTAINER_FACTORY_SERVICE = new ContainerFactoryService(); public static ContainerFactoryService getInstance() { return CONTAINER_FACTORY_SERVICE; } protected final Collection providers = ExtensionService.getInstance().load(ContainerFactory.class); public ContainerFactory get(API api, Path rootPath) { for (ContainerFactory containerFactory : providers) { if (containerFactory.accept(api, rootPath)) { return containerFactory; } } return null; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/extension/ExtensionService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.extension; import org.jd.gui.util.exception.ExceptionUtil; import java.io.File; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.*; public class ExtensionService { protected static final ExtensionService EXTENSION_SERVICE = new ExtensionService(); protected static final UrlComparator URL_COMPARATOR = new UrlComparator(); protected ClassLoader extensionClassLoader; public static ExtensionService getInstance() { return EXTENSION_SERVICE; } protected ExtensionService() { try { URI jarUri = ExtensionService.class.getProtectionDomain().getCodeSource().getLocation().toURI(); File baseDirectory = new File(jarUri).getParentFile(); File extDirectory = new File(baseDirectory, "ext"); if (extDirectory.exists() && extDirectory.isDirectory()) { ArrayList urls = new ArrayList<>(); searchJarAndMetaInf(urls, extDirectory); if (!urls.isEmpty()) { URL[] array = urls.toArray(new URL[urls.size()]); Arrays.sort(array, URL_COMPARATOR); extensionClassLoader = new URLClassLoader(array, ExtensionService.class.getClassLoader()); } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } extensionClassLoader = ExtensionService.class.getClassLoader(); } protected void searchJarAndMetaInf(List urls, File directory) throws Exception { File metaInf = new File(directory, "META-INF"); if (metaInf.exists() && metaInf.isDirectory()) { urls.add(directory.toURI().toURL()); } else { for (File child : directory.listFiles()) { if (child.isDirectory()) { searchJarAndMetaInf(urls, child); } else if (child.getName().toLowerCase().endsWith(".jar")) { urls.add(new URL("jar", "", child.toURI().toURL().toString() + "!/")); } } } } public Collection load(Class service) { ArrayList list = new ArrayList<>(); Iterator iterator = ServiceLoader.load(service, extensionClassLoader).iterator(); while (iterator.hasNext()) { list.add(iterator.next()); } return list; } protected static class UrlComparator implements Comparator { @Override public int compare(URL url1, URL url2) { return url1.getPath().compareTo(url2.getPath()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.FileLoader; import java.io.File; import java.util.Collection; import java.util.HashMap; public class FileLoaderService { protected static final FileLoaderService FILE_LOADER_SERVICE = new FileLoaderService(); public static FileLoaderService getInstance() { return FILE_LOADER_SERVICE; } protected final Collection providers = ExtensionService.getInstance().load(FileLoader.class); protected HashMap mapProviders = new HashMap<>(); protected FileLoaderService() { for (FileLoader provider : providers) { for (String extension : provider.getExtensions()) { mapProviders.put(extension, provider); } } } public FileLoader get(API api, File file) { String name = file.getName(); int lastDot = name.lastIndexOf('.'); String extension = name.substring(lastDot+1); FileLoader provider = mapProviders.get(extension); return provider; } public HashMap getMapProviders() { return mapProviders; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/indexer/IndexerService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.Indexer; import java.util.Collection; import java.util.HashMap; public class IndexerService { protected static final IndexerService INDEXER_SERVICE = new IndexerService(); public static IndexerService getInstance() { return INDEXER_SERVICE; } protected HashMap mapProviders = new HashMap<>(); protected IndexerService() { Collection providers = ExtensionService.getInstance().load(Indexer.class); for (Indexer provider : providers) { for (String selector : provider.getSelectors()) { Indexers indexers = mapProviders.get(selector); if (indexers == null) { mapProviders.put(selector, indexers=new Indexers()); } indexers.add(provider); } } } public Indexer get(Container.Entry entry) { Indexer indexer = get(entry.getContainer().getType(), entry); return (indexer != null) ? indexer : get("*", entry); } protected Indexer get(String containerType, Container.Entry entry) { String path = entry.getPath(); String type = entry.isDirectory() ? "dir" : "file"; String prefix = containerType + ':' + type; Indexer indexer = null; Indexers indexers = mapProviders.get(prefix + ':' + path); if (indexers != null) { indexer = indexers.match(path); } if (indexer == null) { int lastSlashIndex = path.lastIndexOf('/'); String name = path.substring(lastSlashIndex+1); indexers = mapProviders.get(prefix + ":*/" + name); if (indexers != null) { indexer = indexers.match(path); } if (indexer == null) { int index = name.lastIndexOf('.'); if (index != -1) { String extension = name.substring(index + 1); indexers = mapProviders.get(prefix + ":*." + extension); if (indexers != null) { indexer = indexers.match(path); } } if (indexer == null) { indexers = mapProviders.get(prefix + ":*"); if (indexers != null) { indexer = indexers.match(path); } } } } return indexer; } protected static class Indexers { protected HashMap indexers = new HashMap<>(); protected Indexer defaultIndexer; public void add(Indexer indexer) { if (indexer.getPathPattern() != null) { indexers.put(indexer.getPathPattern().pattern(), indexer); } else { defaultIndexer = indexer; } } public Indexer match(String path) { for (Indexer indexer : indexers.values()) { if (indexer.getPathPattern().matcher(path).matches()) { return indexer; } } return defaultIndexer; } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.mainpanel; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContentIndexable; import org.jd.gui.api.feature.SourcesSavable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.Indexer; import org.jd.gui.spi.PanelFactory; import org.jd.gui.spi.SourceSaver; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.component.panel.TreeTabbedPanel; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; public class ContainerPanelFactoryProvider implements PanelFactory { protected static final String[] TYPES = { "default" }; @Override public String[] getTypes() { return TYPES; } @Override @SuppressWarnings("unchecked") public T make(API api, Container container) { return (T)new ContainerPanel(api, container); } protected class ContainerPanel extends TreeTabbedPanel implements ContentIndexable, SourcesSavable { protected Container.Entry entry; public ContainerPanel(API api, Container container) { super(api, container.getRoot().getParent().getUri()); this.entry = container.getRoot().getParent(); DefaultMutableTreeNode root = new DefaultMutableTreeNode(); for (Container.Entry entry : container.getRoot().getChildren()) { TreeNodeFactory factory = api.getTreeNodeFactory(entry); if (factory != null) { root.add(factory.make(api, entry)); } } tree.setModel(new DefaultTreeModel(root)); } // --- ContentIndexable --- // @Override public Indexes index(API api) { HashMap> map = new HashMap<>(); DelegatedMapMapWithDefault mapWithDefault = new DelegatedMapMapWithDefault(map); // Index populating value automatically Indexes indexesWithDefault = name -> mapWithDefault.get(name); // Index entry Indexer indexer = api.getIndexer(entry); if (indexer != null) { indexer.index(api, entry, indexesWithDefault); } // To prevent memory leaks, return an index without the 'populate' behaviour return name -> map.get(name); } // --- SourcesSavable --- // @Override public String getSourceFileName() { SourceSaver saver = api.getSourceSaver(entry); if (saver != null) { String path = saver.getSourcePath(entry); int index = path.lastIndexOf('/'); return path.substring(index+1); } else { return null; } } @Override public int getFileCount() { SourceSaver saver = api.getSourceSaver(entry); return (saver != null) ? saver.getFileCount(api, entry) : 0; } @Override public void save(API api, Controller controller, Listener listener, Path path) { try { Path parentPath = path.getParent(); if ((parentPath != null) && !Files.exists(parentPath)) { Files.createDirectories(parentPath); } URI uri = path.toUri(); URI archiveUri = new URI("jar:" + uri.getScheme(), uri.getHost(), uri.getPath() + "!/", null); try (FileSystem archiveFs = FileSystems.newFileSystem(archiveUri, Collections.singletonMap("create", "true"))) { Path archiveRootPath = archiveFs.getPath("/"); SourceSaver saver = api.getSourceSaver(entry); if (saver != null) { saver.saveContent( api, () -> controller.isCancelled(), (p) -> listener. pathSaved(p), archiveRootPath, archiveRootPath, entry); } } } catch (URISyntaxException|IOException e) { assert ExceptionUtil.printStackTrace(e); } } } protected static class DelegatedMap implements Map { protected Map map; public DelegatedMap(Map map) { this.map = map; } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean containsKey(Object o) { return map.containsKey(o); } @Override public boolean containsValue(Object o) { return map.containsValue(o); } @Override public V get(Object o) { return map.get(o); } @Override public V put(K k, V v) { return map.put(k, v); } @Override public V remove(Object o) { return map.remove(o); } @Override public void putAll(Map map) { this.map.putAll(map); } @Override public void clear() { map.clear(); } @Override public Set keySet() { return map.keySet(); } @Override public Collection values() { return map.values(); } @Override public Set> entrySet() { return map.entrySet(); } @Override public boolean equals(Object o) { return map.equals(o); } @Override public int hashCode() { return map.hashCode(); } } protected static class DelegatedMapWithDefault extends DelegatedMap { public DelegatedMapWithDefault(Map map) { super(map); } @Override public Collection get(Object o) { Collection value = map.get(o); if (value == null) { String key = o.toString(); map.put(key, value=new ArrayList()); } return value; } } protected static class DelegatedMapMapWithDefault extends DelegatedMap> { protected HashMap> wrappers = new HashMap<>(); public DelegatedMapMapWithDefault(Map> map) { super(map); } @Override public Map get(Object o) { Map value = wrappers.get(o); if (value == null) { String key = o.toString(); HashMap m = new HashMap<>(); map.put(key, m); wrappers.put(key, value=new DelegatedMapWithDefault(m)); } return value; } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.mainpanel; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.PanelFactory; import java.util.Collection; import java.util.HashMap; public class PanelFactoryService { protected static final PanelFactoryService PANEL_FACTORY_SERVICE = new PanelFactoryService(); public static PanelFactoryService getInstance() { return PANEL_FACTORY_SERVICE; } protected HashMap mapProviders = new HashMap<>(); protected PanelFactoryService() { Collection providers = ExtensionService.getInstance().load(PanelFactory.class); for (PanelFactory provider : providers) { for (String type : provider.getTypes()) { mapProviders.put(type, provider); } } } public PanelFactory get(Container container) { PanelFactory factory = mapProviders.get(container.getType()); return (factory != null) ? factory : mapProviders.get("default"); } } ================================================ FILE: app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.pastehandler; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.PasteHandler; import java.util.Collection; public class PasteHandlerService { protected static final PasteHandlerService PASTE_HANDLER_SERVICE = new PasteHandlerService(); public static PasteHandlerService getInstance() { return PASTE_HANDLER_SERVICE; } protected final Collection providers = ExtensionService.getInstance().load(PasteHandler.class); public PasteHandler get(Object obj) { for (PasteHandler provider : providers) { if (provider.accept(obj)) { return provider; } } return null; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/platform/PlatformService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.platform; public class PlatformService { protected static final PlatformService PLATFORM_SERVICE = new PlatformService(); public enum OS { Linux, MacOSX, Windows } protected OS os; protected PlatformService() { String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("windows")) { os = OS.Windows; } else if (osName.contains("mac os")) { os = OS.MacOSX; } else { os = OS.Linux; } } public static PlatformService getInstance() { return PLATFORM_SERVICE; } public OS getOs() { return os; } public boolean isLinux() { return os == OS.Linux; } public boolean isMac() { return os == OS.MacOSX; } public boolean isWindows() { return os == OS.Windows; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.PreferencesPanel; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; public class PreferencesPanelService { protected static final PreferencesPanelService PREFERENCES_PANEL_SERVICE = new PreferencesPanelService(); public static PreferencesPanelService getInstance() { return PREFERENCES_PANEL_SERVICE; } protected final Collection providers; protected PreferencesPanelService() { Collection list = ExtensionService.getInstance().load(PreferencesPanel.class); Iterator iterator = list.iterator(); while (iterator.hasNext()) { if (!iterator.next().isActivated()) { iterator.remove(); } } HashMap map = new HashMap<>(); for (PreferencesPanel panel : list) { map.put(panel.getPreferencesGroupTitle() + '$' + panel.getPreferencesPanelTitle(), panel); } providers = map.values(); } public Collection getProviders() { return providers; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.service.platform.PlatformService; import org.jd.gui.spi.PreferencesPanel; import javax.swing.*; import java.awt.*; import java.util.Map; /** * Single instance is the default mode on Mac OSX, so this panel is not activated. */ public class UISingleInstancePreferencesProvider extends JPanel implements PreferencesPanel { protected static final String SINGLE_INSTANCE = "UIMainWindowPreferencesProvider.singleInstance"; protected JCheckBox singleInstanceTabsCheckBox; public UISingleInstancePreferencesProvider() { super(new GridLayout(0,1)); singleInstanceTabsCheckBox = new JCheckBox("Single instance"); add(singleInstanceTabsCheckBox); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "User Interface"; } @Override public String getPreferencesPanelTitle() { return "Main window"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) {} @Override public boolean isActivated() { return !PlatformService.getInstance().isMac(); } @Override public void loadPreferences(Map preferences) { singleInstanceTabsCheckBox.setSelected("true".equals(preferences.get(SINGLE_INSTANCE))); } @Override public void savePreferences(Map preferences) { preferences.put(SINGLE_INSTANCE, Boolean.toString(singleInstanceTabsCheckBox.isSelected())); } @Override public boolean arePreferencesValid() { return true; } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} } ================================================ FILE: app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.service.platform.PlatformService; import org.jd.gui.spi.PreferencesPanel; import javax.swing.*; import java.awt.*; import java.util.Map; /** * JTabbedPane.WRAP_TAB_LAYOUT is not supported by Aqua L&F. * This panel is not activated on Mac OSX. */ public class UITabsPreferencesProvider extends JPanel implements PreferencesPanel { protected static final String TAB_LAYOUT = "UITabsPreferencesProvider.singleLineTabs"; protected JCheckBox singleLineTabsCheckBox; public UITabsPreferencesProvider() { super(new GridLayout(0,1)); singleLineTabsCheckBox = new JCheckBox("Tabs on a single line"); add(singleLineTabsCheckBox); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "User Interface"; } @Override public String getPreferencesPanelTitle() { return "Tabs"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) {} @Override public boolean isActivated() { return !PlatformService.getInstance().isMac(); } @Override public void loadPreferences(Map preferences) { singleLineTabsCheckBox.setSelected("true".equals(preferences.get(TAB_LAYOUT))); } @Override public void savePreferences(Map preferences) { preferences.put(TAB_LAYOUT, Boolean.toString(singleLineTabsCheckBox.isSelected())); } @Override public boolean arePreferencesValid() { return true; } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} } ================================================ FILE: app/src/main/java/org/jd/gui/service/sourceloader/SourceLoaderService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourceloader; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.SourceLoader; import java.io.File; import java.util.Collection; public class SourceLoaderService { protected static final SourceLoaderService SOURCE_LOADER_SERVICE = new SourceLoaderService(); public static SourceLoaderService getInstance() { return SOURCE_LOADER_SERVICE; } protected Collection providers = ExtensionService.getInstance().load(SourceLoader.class); public String getSource(API api, Container.Entry entry) { for (SourceLoader provider : providers) { String source = provider.getSource(api, entry); if ((source != null) && !source.isEmpty()) { return source; } } return null; } public String loadSource(API api, Container.Entry entry) { for (SourceLoader provider : providers) { String source = provider.loadSource(api, entry); if ((source != null) && !source.isEmpty()) { return source; } } return null; } public File getSourceFile(API api, Container.Entry entry) { for (SourceLoader provider : providers) { File file = provider.loadSourceFile(api, entry); if (file != null) { return file; } } return null; } } ================================================ FILE: app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.SourceSaver; import java.util.Collection; import java.util.HashMap; public class SourceSaverService { protected static final SourceSaverService SOURCE_SAVER_SERVICE = new SourceSaverService(); public static SourceSaverService getInstance() { return SOURCE_SAVER_SERVICE; } protected HashMap mapProviders = new HashMap<>(); protected SourceSaverService() { Collection providers = ExtensionService.getInstance().load(SourceSaver.class); for (SourceSaver provider : providers) { for (String selector : provider.getSelectors()) { SourceSavers savers = mapProviders.get(selector); if (savers == null) { mapProviders.put(selector, savers=new SourceSavers()); } savers.add(provider); } } } public SourceSaver get(Container.Entry entry) { SourceSaver saver = get(entry.getContainer().getType(), entry); return (saver != null) ? saver : get("*", entry); } protected SourceSaver get(String containerType, Container.Entry entry) { String path = entry.getPath(); String type = entry.isDirectory() ? "dir" : "file"; String prefix = containerType + ':' + type; SourceSaver saver = null; SourceSavers savers = mapProviders.get(prefix + ':' + path); if (savers != null) { saver = savers.match(path); } if (saver == null) { int lastSlashIndex = path.lastIndexOf('/'); String name = path.substring(lastSlashIndex+1); savers = mapProviders.get(prefix + ":*/" + path); if (savers != null) { saver = savers.match(path); } if (saver == null) { int index = name.lastIndexOf('.'); if (index != -1) { String extension = name.substring(index + 1); savers = mapProviders.get(prefix + ":*." + extension); if (savers != null) { saver = savers.match(path); } } if (saver == null) { savers = mapProviders.get(prefix + ":*"); if (savers != null) { saver = savers.match(path); } } } } return saver; } protected static class SourceSavers { protected HashMap savers = new HashMap<>(); protected SourceSaver defaultSaver; void add(SourceSaver saver) { if (saver.getPathPattern() != null) { savers.put(saver.getPathPattern().pattern(), saver); } else { defaultSaver = saver; } } SourceSaver match(String path) { for (SourceSaver saver : savers.values()) { if (saver.getPathPattern().matcher(path).matches()) { return saver; } } return defaultSaver; } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.TreeNodeFactory; import java.util.Collection; import java.util.HashMap; public class TreeNodeFactoryService { protected static final TreeNodeFactoryService TREE_NODE_FACTORY_SERVICE = new TreeNodeFactoryService(); public static TreeNodeFactoryService getInstance() { return TREE_NODE_FACTORY_SERVICE; } protected HashMap mapProviders = new HashMap<>(); protected TreeNodeFactoryService() { Collection providers = ExtensionService.getInstance().load(TreeNodeFactory.class); for (TreeNodeFactory provider : providers) { for (String selector : provider.getSelectors()) { TreeNodeFactories factories = mapProviders.get(selector); if (factories == null) { mapProviders.put(selector, factories=new TreeNodeFactories()); } factories.add(provider); } } } public TreeNodeFactory get(Container.Entry entry) { TreeNodeFactory factory = get(entry.getContainer().getType(), entry); return (factory != null) ? factory : get("*", entry); } protected TreeNodeFactory get(String containerType, Container.Entry entry) { String path = entry.getPath(); String type = entry.isDirectory() ? "dir" : "file"; String prefix = containerType + ':' + type + ':'; TreeNodeFactory factory = null; TreeNodeFactories factories = mapProviders.get(prefix + path); if (factories != null) { factory = factories.match(path); } if (factory == null) { int lastSlashIndex = path.lastIndexOf('/'); String name = path.substring(lastSlashIndex+1); factories = mapProviders.get(prefix + "*/" + name); if (factories != null) { factory = factories.match(path); } if (factory == null) { int index = name.lastIndexOf('.'); if (index != -1) { String extension = name.substring(index + 1); factories = mapProviders.get(prefix + "*." + extension); if (factories != null) { factory = factories.match(path); } } if (factory == null) { factories = mapProviders.get(prefix + "*"); if (factories != null) { factory = factories.match(path); } } } } return factory; } protected static class TreeNodeFactories { protected HashMap factories = new HashMap<>(); protected TreeNodeFactory defaultFactory; public void add(TreeNodeFactory factory) { if (factory.getPathPattern() != null) { factories.put(factory.getPathPattern().pattern(), factory); } else { defaultFactory = factory; } } public TreeNodeFactory match(String path) { for (TreeNodeFactory factory : factories.values()) { if (factory.getPathPattern().matcher(path).matches()) { return factory; } } return defaultFactory; } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.type; import org.jd.gui.api.model.Container; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.TypeFactory; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TypeFactoryService { protected static final TypeFactoryService TYPE_FACTORY_SERVICE = new TypeFactoryService(); protected Map mapProviders; public static TypeFactoryService getInstance() { return TYPE_FACTORY_SERVICE; } protected TypeFactoryService() { Collection providers = ExtensionService.getInstance().load(TypeFactory.class); mapProviders = new HashMap<>(); for (TypeFactory provider : providers) { for (String selector : provider.getSelectors()) { TypeFactories typeFactories = mapProviders.get(selector); if (typeFactories == null) { mapProviders.put(selector, typeFactories=new TypeFactories()); } typeFactories.add(provider); } } } public TypeFactory get(Container.Entry entry) { TypeFactory typeFactory = get(entry.getContainer().getType(), entry); return (typeFactory != null) ? typeFactory : get("*", entry); } public TypeFactory get(String containerType, Container.Entry entry) { String path = entry.getPath(); String type = entry.isDirectory() ? "dir" : "file"; String prefix = containerType + ':' + type + ':'; TypeFactories typeFactories = mapProviders.get(prefix + path); TypeFactory factory = null; if (typeFactories != null) { factory = typeFactories.match(path); } if (factory == null) { int lastSlashIndex = path.lastIndexOf('/'); String name = path.substring(lastSlashIndex+1); typeFactories = mapProviders.get(prefix + "*/" + name); if (typeFactories != null) { factory = typeFactories.match(path); } if (factory == null) { int index = name.lastIndexOf('.'); if (index != -1) { String extension = name.substring(index + 1); typeFactories = mapProviders.get(prefix + "*." + extension); if (typeFactories != null) { factory = typeFactories.match(path); } } if (factory == null) { typeFactories = mapProviders.get(prefix + '*'); if (typeFactories != null) { factory = typeFactories.match(path); } } } } return factory; } protected static class TypeFactories { protected HashMap factories = new HashMap<>(); protected TypeFactory defaultFactory; public void add(TypeFactory factory) { Pattern pathPattern = factory.getPathPattern(); if (pathPattern != null) { factories.put(pathPattern.pattern(), factory); } else { defaultFactory = factory; } } public TypeFactory match(String path) { for (TypeFactory factory : factories.values()) { Matcher matcher = factory.getPathPattern().matcher(path); if (matcher.matches()) { return factory; } } return defaultFactory; } } } ================================================ FILE: app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.uriloader; import org.jd.gui.api.API; import org.jd.gui.service.extension.ExtensionService; import org.jd.gui.spi.UriLoader; import java.net.URI; import java.util.Collection; import java.util.HashMap; public class UriLoaderService { protected static final UriLoaderService URI_LOADER_SERVICE = new UriLoaderService(); public static UriLoaderService getInstance() { return URI_LOADER_SERVICE; } protected HashMap mapProviders = new HashMap<>(); protected UriLoaderService() { Collection providers = ExtensionService.getInstance().load(UriLoader.class); for (UriLoader provider : providers) { for (String scheme : provider.getSchemes()) { mapProviders.put(scheme, provider); } } } public UriLoader get(API api, URI uri) { UriLoader provider = mapProviders.get(uri.getScheme()); if (provider.accept(api, uri)) { return provider; } else { return null; } } } ================================================ FILE: app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.exception; public class ExceptionUtil { public static boolean printStackTrace(Throwable throwable) { throwable.printStackTrace(); return true; } } ================================================ FILE: app/src/main/java/org/jd/gui/util/function/TriConsumer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.function; import java.util.Objects; @FunctionalInterface public interface TriConsumer { void accept(T t, U u, V v); default TriConsumer andThen(TriConsumer after) { Objects.requireNonNull(after); return (a, b, c) -> { accept(a, b, c); after.accept(a, b, c); }; } } ================================================ FILE: app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.net; import org.jd.gui.util.exception.ExceptionUtil; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.function.Consumer; public class InterProcessCommunicationUtil { protected static final int PORT = 2015_6; public static void listen(final Consumer consumer) throws Exception { final ServerSocket listener = new ServerSocket(PORT); Runnable runnable = new Runnable() { @Override public void run() { while (true) { try (Socket socket = listener.accept(); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) { // Receive args from another JD-GUI instance String[] args = (String[])ois.readObject(); consumer.accept(args); } catch (IOException|ClassNotFoundException e) { assert ExceptionUtil.printStackTrace(e); } } } }; new Thread(runnable).start(); } public static void send(String[] args) { try (Socket socket = new Socket(InetAddress.getLocalHost(), PORT); ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) { // Send args to the main JD-GUI instance oos.writeObject(args); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: app/src/main/java/org/jd/gui/util/net/UriUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.net; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.api.model.Type; import org.jd.gui.service.type.TypeFactoryService; import org.jd.gui.spi.TypeFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.net.URI; import java.net.URISyntaxException; import java.util.Collection; import java.util.concurrent.Future; public class UriUtil { /* * Convert inner entry URI to outer entry uri with a fragment. Example: * file://codebase/a/b/c/D$E.class => file://codebase/a/b/c/D.class#typeDeclaration=D$E */ public static URI createURI(API api, Collection> collectionOfFutureIndexes, Container.Entry entry, String query, String fragment) { URI uri = entry.getUri(); try { String path = uri.getPath(); TypeFactory typeFactory = TypeFactoryService.getInstance().get(entry); if (typeFactory != null) { Type type = typeFactory.make(api, entry, fragment); if (type != null) { path = getOuterPath(collectionOfFutureIndexes, entry, type); } } return new URI(uri.getScheme(), uri.getHost(), path, query, fragment); } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); return uri; } } @SuppressWarnings("unchecked") protected static String getOuterPath(Collection> collectionOfFutureIndexes, Container.Entry entry, Type type) { String outerName = type.getOuterName(); if (outerName != null) { try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Collection outerEntries = futureIndexes.get().getIndex("typeDeclarations").get(outerName); if (outerEntries != null) { for (Container.Entry outerEntry : outerEntries) { if (outerEntry.getContainer() == entry.getContainer()) { return outerEntry.getUri().getPath(); } } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } return entry.getUri().getPath(); } } ================================================ FILE: app/src/main/java/org/jd/gui/util/swing/SwingUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.swing; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * See: https://www.ailis.de/~k/archives/67-Workaround-for-borderless-Java-Swing-menus-on-Linux.html */ public class SwingUtil { /* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to */ /** * Swing menus are looking pretty bad on Linux when the GTK LaF is used (See * bug #6925412). It will most likely never be fixed anytime soon so this * method provides a workaround for it. It uses reflection to change the GTK * style objects of Swing so popup menu borders have a minimum thickness of * 1 and menu separators have a minimum vertical thickness of 1. */ public static void installGtkPopupBugWorkaround() { // Get current look-and-feel implementation class LookAndFeel laf = UIManager.getLookAndFeel(); Class lafClass = laf.getClass(); // Do nothing when not using the problematic LaF if (!lafClass.getName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")) return; // We do reflection from here on. Failure is silently ignored. The // workaround is simply not installed when something goes wrong here try { // Access the GTK style factory Field field = lafClass.getDeclaredField("styleFactory"); boolean accessible = field.isAccessible(); field.setAccessible(true); Object styleFactory = field.get(laf); field.setAccessible(accessible); // Fix the horizontal and vertical thickness of popup menu style Object style = getGtkStyle(styleFactory, new JPopupMenu(), "POPUP_MENU"); fixGtkThickness(style, "yThickness"); fixGtkThickness(style, "xThickness"); // Fix the vertical thickness of the popup menu separator style style = getGtkStyle(styleFactory, new JSeparator(), "POPUP_MENU_SEPARATOR"); fixGtkThickness(style, "yThickness"); } catch (Exception e) { // Silently ignored. Workaround can't be applied. assert ExceptionUtil.printStackTrace(e); } } /** * Called internally by installGtkPopupBugWorkaround to fix the thickness * of a GTK style field by setting it to a minimum value of 1. * * @param style * The GTK style object. * @param fieldName * The field name. * @throws Exception * When reflection fails. */ private static void fixGtkThickness(Object style, String fieldName) throws Exception { Field field = style.getClass().getDeclaredField(fieldName); boolean accessible = field.isAccessible(); field.setAccessible(true); field.setInt(style, Math.max(1, field.getInt(style))); field.setAccessible(accessible); } /** * Called internally by installGtkPopupBugWorkaround. Returns a specific * GTK style object. * * @param styleFactory * The GTK style factory. * @param component * The target component of the style. * @param regionName * The name of the target region of the style. * @return The GTK style. * @throws Exception * When reflection fails. */ private static Object getGtkStyle(Object styleFactory, JComponent component, String regionName) throws Exception { // Create the region object Class regionClass = Class.forName("javax.swing.plaf.synth.Region"); Field field = regionClass.getField(regionName); Object region = field.get(regionClass); // Get and return the style Class styleFactoryClass = styleFactory.getClass(); Method method = styleFactoryClass.getMethod("getStyle", JComponent.class, regionClass); boolean accessible = method.isAccessible(); method.setAccessible(true); Object style = method.invoke(styleFactory, component, region); method.setAccessible(accessible); return style; } public static void invokeLater(Runnable runnable) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } public static Image getImage(String iconPath) { return Toolkit.getDefaultToolkit().getImage(SwingUtil.class.getResource(iconPath)); } public static ImageIcon newImageIcon(String iconPath) { return new ImageIcon(getImage(iconPath)); } public static Action newAction(String name, boolean enable, ActionListener listener) { Action action = new AbstractAction(name) { @Override public void actionPerformed(ActionEvent actionEvent) { listener.actionPerformed(actionEvent); } }; action.setEnabled(enable); return action; } public static Action newAction(String name, ImageIcon icon, boolean enable, ActionListener listener) { Action action = newAction(name, enable, listener); action.putValue(Action.SMALL_ICON, icon); return action; } public static Action newAction(ImageIcon icon, boolean enable, ActionListener listener) { Action action = newAction(null, icon, enable, listener); action.putValue(Action.SMALL_ICON, icon); return action; } public static Action newAction(String name, ImageIcon icon, boolean enable, String shortDescription, ActionListener listener) { Action action = newAction(name, icon, enable, listener); action.putValue(Action.SHORT_DESCRIPTION, shortDescription); return action; } public static Action newAction(String name, boolean enable, String shortDescription, ActionListener listener) { Action action = newAction(name, enable, listener); action.putValue(Action.SHORT_DESCRIPTION, shortDescription); return action; } } ================================================ FILE: app/src/main/java/org/jd/gui/view/AboutView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.swing.SwingUtil; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.Manifest; public class AboutView { protected JDialog aboutDialog; protected JButton aboutOkButton; public AboutView(JFrame mainFrame) { // Build GUI SwingUtil.invokeLater(() -> { aboutDialog = new JDialog(mainFrame, "About Java Decompiler", false); aboutDialog.setResizable(false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); panel.setLayout(new BorderLayout()); aboutDialog.add(panel); Box vbox = Box.createVerticalBox(); panel.add(vbox, BorderLayout.NORTH); JPanel subpanel = new JPanel(); vbox.add(subpanel); subpanel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); subpanel.setBackground(Color.WHITE); subpanel.setLayout(new BorderLayout()); JLabel logo = new JLabel(new ImageIcon(SwingUtil.getImage("/org/jd/gui/images/jd_icon_64.png"))); logo.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); subpanel.add(logo, BorderLayout.WEST); Box subvbox = Box.createVerticalBox(); subvbox.setBorder(BorderFactory.createEmptyBorder(15,0,15,15)); subpanel.add(subvbox, BorderLayout.EAST); Box hbox = Box.createHorizontalBox(); subvbox.add(hbox); JLabel mainLabel = new JLabel("Java Decompiler"); mainLabel.setFont(UIManager.getFont("Label.font").deriveFont(Font.BOLD, 14)); hbox.add(mainLabel); hbox.add(Box.createHorizontalGlue()); hbox = Box.createHorizontalBox(); subvbox.add(hbox); JPanel subsubpanel = new JPanel(); hbox.add(subsubpanel); subsubpanel.setLayout(new GridLayout(2,2)); subsubpanel.setOpaque(false); subsubpanel.setBorder(BorderFactory.createEmptyBorder(5,10,5,5)); String jdGuiVersion = "SNAPSHOT"; String jdCoreVersion = "SNAPSHOT"; try { Enumeration enumeration = AboutView.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); while (enumeration.hasMoreElements()) { try (InputStream is = enumeration.nextElement().openStream()) { Attributes attributes = new Manifest(is).getMainAttributes(); String attribute = attributes.getValue("JD-GUI-Version"); if (attribute != null) { jdGuiVersion = attribute; } attribute = attributes.getValue("JD-Core-Version"); if (attribute != null) { jdCoreVersion = attribute; } } } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } subsubpanel.add(new JLabel("JD-GUI")); subsubpanel.add(new JLabel("version " + jdGuiVersion)); subsubpanel.add(new JLabel("JD-Core")); subsubpanel.add(new JLabel("version " + jdCoreVersion)); hbox.add(Box.createHorizontalGlue()); hbox = Box.createHorizontalBox(); hbox.add(new JLabel("Copyright © 2008, 2019 Emmanuel Dupuy")); hbox.add(Box.createHorizontalGlue()); subvbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); hbox = Box.createHorizontalBox(); panel.add(hbox, BorderLayout.SOUTH); hbox.add(Box.createHorizontalGlue()); aboutOkButton = new JButton(" Ok "); Action aboutOkActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { aboutDialog.setVisible(false); } }; aboutOkButton.addActionListener(aboutOkActionListener); hbox.add(aboutOkButton); hbox.add(Box.createHorizontalGlue()); // Last setup JRootPane rootPane = aboutDialog.getRootPane(); rootPane.setDefaultButton(aboutOkButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "AboutView.ok"); rootPane.getActionMap().put("AboutView.ok", aboutOkActionListener); // Prepare to display aboutDialog.pack(); }); } public void show() { SwingUtil.invokeLater(() -> { // Show aboutDialog.setLocationRelativeTo(aboutDialog.getParent()); aboutDialog.setVisible(true); aboutOkButton.requestFocus(); }); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/GoToView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.api.feature.LineNumberNavigable; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.util.swing.SwingUtil; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.function.IntConsumer; public class GoToView { protected JDialog goToDialog; protected JLabel goToEnterLineNumberLabel; protected JTextField goToEnterLineNumberTextField; protected JLabel goToEnterLineNumberErrorLabel; protected LineNumberNavigable navigator; protected IntConsumer okCallback; public GoToView(Configuration configuration, JFrame mainFrame) { // Build GUI SwingUtil.invokeLater(() -> { goToDialog = new JDialog(mainFrame, "Go to Line", false); goToDialog.setResizable(false); Box vbox = Box.createVerticalBox(); vbox.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); goToDialog.add(vbox); // First label "Enter line number (1..xxx):" Box hbox = Box.createHorizontalBox(); hbox.add(goToEnterLineNumberLabel = new JLabel()); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); // Text field vbox.add(goToEnterLineNumberTextField = new JTextField(30)); vbox.add(Box.createVerticalStrut(10)); // Error label hbox = Box.createHorizontalBox(); hbox.add(goToEnterLineNumberErrorLabel = new JLabel(" ")); goToEnterLineNumberTextField.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { if (! Character.isDigit(e.getKeyChar())) { e.consume(); } } }); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(15)); // Buttons "Ok" and "Cancel" hbox = Box.createHorizontalBox(); hbox.add(Box.createHorizontalGlue()); JButton goToOkButton = new JButton(" Ok "); hbox.add(goToOkButton); goToOkButton.setEnabled(false); goToOkButton.addActionListener(e -> { okCallback.accept(Integer.valueOf(goToEnterLineNumberTextField.getText())); goToDialog.setVisible(false); }); hbox.add(Box.createHorizontalStrut(5)); JButton goToCancelButton = new JButton("Cancel"); hbox.add(goToCancelButton); Action goToCancelActionListener = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { goToDialog.setVisible(false); } }; goToCancelButton.addActionListener(goToCancelActionListener); vbox.add(hbox); vbox.add(Box.createVerticalStrut(13)); // Last setup JRootPane rootPane = goToDialog.getRootPane(); rootPane.setDefaultButton(goToOkButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeView.cancel"); rootPane.getActionMap().put("OpenTypeView.cancel", goToCancelActionListener); // Add main listener goToEnterLineNumberTextField.getDocument().addDocumentListener(new DocumentListener() { protected Color backgroundColor = UIManager.getColor("TextField.background"); protected Color errorBackgroundColor = Color.decode(configuration.getPreferences().get("JdGuiPreferences.errorBackgroundColor")); @Override public void insertUpdate(DocumentEvent e) { onTextChange(); } @Override public void removeUpdate(DocumentEvent e) { onTextChange(); } @Override public void changedUpdate(DocumentEvent e) { onTextChange(); } protected void onTextChange() { String text = goToEnterLineNumberTextField.getText(); if (text.length() == 0) { goToOkButton.setEnabled(false); clearErrorMessage(); } else { try { int lineNumber = Integer.valueOf(text); if (lineNumber > navigator.getMaximumLineNumber()) { goToOkButton.setEnabled(false); showErrorMessage("Line number out of range"); } else if (navigator.checkLineNumber(lineNumber)) { goToOkButton.setEnabled(true); clearErrorMessage(); } else { goToOkButton.setEnabled(false); showErrorMessage("Line number not found"); } } catch (NumberFormatException e) { goToOkButton.setEnabled(false); showErrorMessage("Not a number"); } } } protected void showErrorMessage(String message) { goToEnterLineNumberErrorLabel.setText(message); goToEnterLineNumberTextField.setBackground(errorBackgroundColor); } protected void clearErrorMessage() { goToEnterLineNumberErrorLabel.setText(" "); goToEnterLineNumberTextField.setBackground(backgroundColor); } }); // Prepare to display goToDialog.pack(); goToDialog.setLocationRelativeTo(mainFrame); }); } public void show(LineNumberNavigable navigator, IntConsumer okCallback) { this.navigator = navigator; this.okCallback = okCallback; SwingUtil.invokeLater(() -> { // Init goToEnterLineNumberLabel.setText("Enter line number (1.." + navigator.getMaximumLineNumber() + "):"); goToEnterLineNumberTextField.setText(""); // Show goToDialog.setVisible(true); goToEnterLineNumberTextField.requestFocus(); }); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/MainView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.Constants; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.model.history.History; import org.jd.gui.service.platform.PlatformService; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.component.IconButton; import org.jd.gui.view.component.panel.MainTabbedPanel; import javax.swing.*; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Consumer; import static org.jd.gui.util.swing.SwingUtil.*; @SuppressWarnings("unchecked") public class MainView implements UriOpenable, PreferencesChangeListener { protected History history; protected Consumer openFilesCallback; protected JFrame mainFrame; protected JMenu recentFiles = new JMenu("Recent Files"); protected Action closeAction; protected Action openTypeAction; protected Action backwardAction; protected Action forwardAction; protected MainTabbedPanel mainTabbedPanel; protected Box findPanel; protected JComboBox findComboBox; protected JCheckBox findCaseSensitive; protected Color findBackgroundColor; protected Color findErrorBackgroundColor; public MainView( Configuration configuration, API api, History history, ActionListener openActionListener, ActionListener closeActionListener, ActionListener saveActionListener, ActionListener saveAllSourcesActionListener, ActionListener exitActionListener, ActionListener copyActionListener, ActionListener pasteActionListener, ActionListener selectAllActionListener, ActionListener findActionListener, ActionListener findPreviousActionListener, ActionListener findNextActionListener, ActionListener findCaseSensitiveActionListener, Runnable findCriteriaChangedCallback, ActionListener openTypeActionListener, ActionListener openTypeHierarchyActionListener, ActionListener goToActionListener, ActionListener backwardActionListener, ActionListener forwardActionListener, ActionListener searchActionListener, ActionListener jdWebSiteActionListener, ActionListener jdGuiIssuesActionListener, ActionListener jdCoreIssuesActionListener, ActionListener preferencesActionListener, ActionListener aboutActionListener, Runnable panelClosedCallback, Consumer currentPageChangedCallback, Consumer openFilesCallback) { this.history = history; this.openFilesCallback = openFilesCallback; // Build GUI invokeLater(() -> { mainFrame = new JFrame("Java Decompiler"); mainFrame.setIconImages(Arrays.asList(getImage("/org/jd/gui/images/jd_icon_32.png"), getImage("/org/jd/gui/images/jd_icon_64.png"), getImage("/org/jd/gui/images/jd_icon_128.png"))); mainFrame.setMinimumSize(new Dimension(Constants.MINIMAL_WIDTH, Constants.MINIMAL_HEIGHT)); mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // Find panel // Action findNextAction = newAction("Next", newImageIcon("/org/jd/gui/images/next_nav.png"), true, findNextActionListener); findPanel = Box.createHorizontalBox(); findPanel.setVisible(false); findPanel.add(new JLabel("Find: ")); findComboBox = new JComboBox(); findComboBox.setEditable(true); JComponent editorComponent = (JComponent)findComboBox.getEditor().getEditorComponent(); editorComponent.addKeyListener(new KeyAdapter() { protected String lastStr = ""; @Override public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_ESCAPE: findPanel.setVisible(false); break; case KeyEvent.VK_ENTER: String str = getFindText(); if (str.length() > 1) { int index = ((DefaultComboBoxModel)findComboBox.getModel()).getIndexOf(str); if(index != -1 ) { findComboBox.removeItemAt(index); } findComboBox.insertItemAt(str, 0); findComboBox.setSelectedIndex(0); findNextAction.actionPerformed(null); } break; default: str = getFindText(); if (! lastStr.equals(str)) { findCriteriaChangedCallback.run(); lastStr = str; } } } }); editorComponent.setOpaque(true); findComboBox.setBackground(this.findBackgroundColor = editorComponent.getBackground()); this.findErrorBackgroundColor = Color.decode(configuration.getPreferences().get("JdGuiPreferences.errorBackgroundColor")); findPanel.add(findComboBox); findPanel.add(Box.createHorizontalStrut(5)); JToolBar toolBar = new JToolBar(); toolBar.setFloatable(false); toolBar.setRollover(true); IconButton findNextButton = new IconButton("Next", newAction(newImageIcon("/org/jd/gui/images/next_nav.png"), true, findNextActionListener)); toolBar.add(findNextButton); toolBar.add(Box.createHorizontalStrut(5)); IconButton findPreviousButton = new IconButton("Previous", newAction(newImageIcon("/org/jd/gui/images/prev_nav.png"), true, findPreviousActionListener)); toolBar.add(findPreviousButton); findPanel.add(toolBar); findCaseSensitive = new JCheckBox(); findCaseSensitive.setAction(newAction("Case sensitive", true, findCaseSensitiveActionListener)); findPanel.add(findCaseSensitive); findPanel.add(Box.createHorizontalGlue()); IconButton findCloseButton = new IconButton(newAction(null, null, true, e -> findPanel.setVisible(false))); findCloseButton.setContentAreaFilled(false); findCloseButton.setIcon(newImageIcon("/org/jd/gui/images/close.gif")); findCloseButton.setRolloverIcon(newImageIcon("/org/jd/gui/images/close_active.gif")); findPanel.add(findCloseButton); if (PlatformService.getInstance().isMac()) { findPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); Border border = BorderFactory.createEmptyBorder(); findNextButton.setBorder(border); findPreviousButton.setBorder(border); findCloseButton.setBorder(border); } else { findPanel.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 2)); } // Actions // boolean browser = Desktop.isDesktopSupported() ? Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) : false; Action openAction = newAction("Open File...", newImageIcon("/org/jd/gui/images/open.png"), true, "Open a file", openActionListener); closeAction = newAction("Close", false, closeActionListener); Action saveAction = newAction("Save", newImageIcon("/org/jd/gui/images/save.png"), false, saveActionListener); Action saveAllSourcesAction = newAction("Save All Sources", newImageIcon("/org/jd/gui/images/save_all.png"), false, saveAllSourcesActionListener); Action exitAction = newAction("Exit", true, "Quit this program", exitActionListener); Action copyAction = newAction("Copy", newImageIcon("/org/jd/gui/images/copy.png"), false, copyActionListener); Action pasteAction = newAction("Paste Log", newImageIcon("/org/jd/gui/images/paste.png"), true, pasteActionListener); Action selectAllAction = newAction("Select all", false, selectAllActionListener); Action findAction = newAction("Find...", false, findActionListener); openTypeAction = newAction("Open Type...", newImageIcon("/org/jd/gui/images/open_type.png"), false, openTypeActionListener); Action openTypeHierarchyAction = newAction("Open Type Hierarchy...", false, openTypeHierarchyActionListener); Action goToAction = newAction("Go to Line...", false, goToActionListener); backwardAction = newAction("Back", newImageIcon("/org/jd/gui/images/backward_nav.png"), false, backwardActionListener); forwardAction = newAction("Forward", newImageIcon("/org/jd/gui/images/forward_nav.png"), false, forwardActionListener); Action searchAction = newAction("Search...", newImageIcon("/org/jd/gui/images/search_src.png"), false, searchActionListener); Action jdWebSiteAction = newAction("JD Web site", browser, "Open JD Web site", jdWebSiteActionListener); Action jdGuiIssuesActionAction = newAction("JD-GUI issues", browser, "Open JD-GUI issues page", jdGuiIssuesActionListener); Action jdCoreIssuesActionAction = newAction("JD-Core issues", browser, "Open JD-Core issues page", jdCoreIssuesActionListener); Action preferencesAction = newAction("Preferences...", newImageIcon("/org/jd/gui/images/preferences.png"), true, "Open the preferences panel", preferencesActionListener); Action aboutAction = newAction("About...", true, "About JD-GUI", aboutActionListener); // Menu // int menuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); menu.add(openAction).setAccelerator(KeyStroke.getKeyStroke('O', menuShortcutKeyMask)); menu.addSeparator(); menu.add(closeAction).setAccelerator(KeyStroke.getKeyStroke('W', menuShortcutKeyMask)); menu.addSeparator(); menu.add(saveAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask)); menu.add(saveAllSourcesAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask|InputEvent.ALT_MASK)); menu.addSeparator(); menu.add(recentFiles); if (!PlatformService.getInstance().isMac()) { menu.addSeparator(); menu.add(exitAction).setAccelerator(KeyStroke.getKeyStroke('X', InputEvent.ALT_MASK)); } menu = new JMenu("Edit"); menuBar.add(menu); menu.add(copyAction).setAccelerator(KeyStroke.getKeyStroke('C', menuShortcutKeyMask)); menu.add(pasteAction).setAccelerator(KeyStroke.getKeyStroke('V', menuShortcutKeyMask)); menu.addSeparator(); menu.add(selectAllAction).setAccelerator(KeyStroke.getKeyStroke('A', menuShortcutKeyMask)); menu.addSeparator(); menu.add(findAction).setAccelerator(KeyStroke.getKeyStroke('F', menuShortcutKeyMask)); menu = new JMenu("Navigation"); menuBar.add(menu); menu.add(openTypeAction).setAccelerator(KeyStroke.getKeyStroke('T', menuShortcutKeyMask)); menu.add(openTypeHierarchyAction).setAccelerator(KeyStroke.getKeyStroke('H', menuShortcutKeyMask)); menu.addSeparator(); menu.add(goToAction).setAccelerator(KeyStroke.getKeyStroke('L', menuShortcutKeyMask)); menu.addSeparator(); menu.add(backwardAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.ALT_MASK)); menu.add(forwardAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.ALT_MASK)); menu = new JMenu("Search"); menuBar.add(menu); menu.add(searchAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask|InputEvent.SHIFT_MASK)); menu = new JMenu("Help"); menuBar.add(menu); if (browser) { menu.add(jdWebSiteAction); menu.add(jdGuiIssuesActionAction); menu.add(jdCoreIssuesActionAction); menu.addSeparator(); } menu.add(preferencesAction).setAccelerator(KeyStroke.getKeyStroke('P', menuShortcutKeyMask|InputEvent.SHIFT_MASK)); if (!PlatformService.getInstance().isMac()) { menu.addSeparator(); menu.add(aboutAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); } mainFrame.setJMenuBar(menuBar); // Icon bar // JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); toolBar = new JToolBar(); toolBar.setFloatable(false); toolBar.setRollover(true); toolBar.add(new IconButton(openAction)); toolBar.addSeparator(); toolBar.add(new IconButton(openTypeAction)); toolBar.add(new IconButton(searchAction)); toolBar.addSeparator(); toolBar.add(new IconButton(backwardAction)); toolBar.add(new IconButton(forwardAction)); panel.add(toolBar, BorderLayout.PAGE_START); mainTabbedPanel = new MainTabbedPanel(api); mainTabbedPanel.getPageChangedListeners().add(new PageChangeListener() { protected JComponent currentPage = null; @Override public void pageChanged(U page) { if (currentPage != page) { // Update current page currentPage = page; currentPageChangedCallback.accept((T)page); invokeLater(() -> { if (page == null) { // Update title mainFrame.setTitle("Java Decompiler"); // Update menu saveAction.setEnabled(false); copyAction.setEnabled(false); selectAllAction.setEnabled(false); openTypeHierarchyAction.setEnabled(false); goToAction.setEnabled(false); // Update find panel findPanel.setVisible(false); } else { // Update title String path = page.getUri().getPath(); int index = path.lastIndexOf('/'); String name = (index == -1) ? path : path.substring(index + 1); mainFrame.setTitle((name != null) ? name + " - Java Decompiler" : "Java Decompiler"); // Update history history.add(page.getUri()); // Update history actions updateHistoryActions(); // Update menu saveAction.setEnabled(page instanceof ContentSavable); copyAction.setEnabled(page instanceof ContentCopyable); selectAllAction.setEnabled(page instanceof ContentSelectable); findAction.setEnabled(page instanceof ContentSearchable); openTypeHierarchyAction.setEnabled(page instanceof FocusedTypeGettable); goToAction.setEnabled(page instanceof LineNumberNavigable); // Update find panel if (findPanel.isVisible()) { findPanel.setVisible(page instanceof ContentSearchable); } } }); } } }); mainTabbedPanel.getTabbedPane().addChangeListener(new ChangeListener() { protected int lastTabCount = 0; @Override public void stateChanged(ChangeEvent e) { int tabCount = mainTabbedPanel.getTabbedPane().getTabCount(); boolean enabled = (tabCount > 0); closeAction.setEnabled(enabled); openTypeAction.setEnabled(enabled); searchAction.setEnabled(enabled); saveAllSourcesAction.setEnabled((mainTabbedPanel.getTabbedPane().getSelectedComponent() instanceof SourcesSavable)); if (tabCount < lastTabCount) { panelClosedCallback.run(); } lastTabCount = tabCount; } }); mainTabbedPanel.preferencesChanged(configuration.getPreferences()); panel.add(mainTabbedPanel, BorderLayout.CENTER); panel.add(findPanel, BorderLayout.PAGE_END); mainFrame.add(panel); }); } public void show(Point location, Dimension size, boolean maximize) { invokeLater(() -> { // Set position, resize and show mainFrame.setLocation(location); mainFrame.setSize(size); mainFrame.setExtendedState(maximize ? JFrame.MAXIMIZED_BOTH : 0); mainFrame.setVisible(true); }); } public JFrame getMainFrame() { return mainFrame; } public void showFindPanel() { invokeLater(() -> { findPanel.setVisible(true); findComboBox.requestFocus(); }); } public void setFindBackgroundColor(boolean wasFound) { invokeLater(() -> { findComboBox.getEditor().getEditorComponent().setBackground(wasFound ? findBackgroundColor : findErrorBackgroundColor); }); } public void addMainPanel(String title, Icon icon, String tip, T component) { invokeLater(() -> { mainTabbedPanel.addPage(title, icon, tip, component); }); } public List getMainPanels() { return mainTabbedPanel.getPages(); } public T getSelectedMainPanel() { return (T)mainTabbedPanel.getTabbedPane().getSelectedComponent(); } public void closeCurrentTab() { invokeLater(() -> { Component component = mainTabbedPanel.getTabbedPane().getSelectedComponent(); if (component instanceof PageClosable) { if (!((PageClosable)component).closePage()) { mainTabbedPanel.removeComponent(component); } } else { mainTabbedPanel.removeComponent(component); } }); } public void updateRecentFilesMenu(List files) { invokeLater(() -> { recentFiles.removeAll(); for (File file : files) { JMenuItem menuItem = new JMenuItem(reduceRecentFilePath(file.getAbsolutePath())); menuItem.addActionListener(e -> openFilesCallback.accept(file)); recentFiles.add(menuItem); } }); } public String getFindText() { Document doc = ((JTextField)findComboBox.getEditor().getEditorComponent()).getDocument(); try { return doc.getText(0, doc.getLength()); } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); return ""; } } public boolean getFindCaseSensitive() { return findCaseSensitive.isSelected(); } public void updateHistoryActions() { invokeLater(() -> { backwardAction.setEnabled(history.canBackward()); forwardAction.setEnabled(history.canForward()); }); } // --- Utils --- // static String reduceRecentFilePath(String path) { int lastSeparatorPosition = path.lastIndexOf(File.separatorChar); if ((lastSeparatorPosition == -1) || (lastSeparatorPosition < Constants.RECENT_FILE_MAX_LENGTH)) { return path; } int length = Constants.RECENT_FILE_MAX_LENGTH/2 - 2; String left = path.substring(0, length); String right = path.substring(path.length() - length); return left + "..." + right; } // --- URIOpener --- // @Override public boolean openUri(URI uri) { boolean success = mainTabbedPanel.openUri(uri); if (success) { closeAction.setEnabled(true); openTypeAction.setEnabled(true); } return success; } // --- PreferencesChangeListener --- // @Override public void preferencesChanged(Map preferences) { mainTabbedPanel.preferencesChanged(preferences); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.api.model.TreeNodeData; import org.jd.gui.api.model.Type; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.function.TriConsumer; import org.jd.gui.util.swing.SwingUtil; import org.jd.gui.view.component.Tree; import org.jd.gui.view.renderer.TreeNodeRenderer; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import java.util.concurrent.Future; public class OpenTypeHierarchyView { protected static final ImageIcon ROOT_CLASS_ICON = new ImageIcon(OpenTypeHierarchyView.class.getClassLoader().getResource("org/jd/gui/images/generate_class.png")); protected static final ImageIcon ROOT_INTERFACE_ICON = new ImageIcon(OpenTypeHierarchyView.class.getClassLoader().getResource("org/jd/gui/images/generate_int.png")); protected static final TreeNodeComparator TREE_NODE_COMPARATOR = new TreeNodeComparator(); protected API api; protected Collection> collectionOfFutureIndexes; protected JDialog openTypeHierarchyDialog; protected Tree openTypeHierarchyTree; protected TriConsumer, String> selectedTypeCallback; public OpenTypeHierarchyView(API api, JFrame mainFrame, TriConsumer, String> selectedTypeCallback) { this.api = api; this.selectedTypeCallback = selectedTypeCallback; // Build GUI SwingUtil.invokeLater(() -> { openTypeHierarchyDialog = new JDialog(mainFrame, "Hierarchy Type", false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); panel.setLayout(new BorderLayout()); openTypeHierarchyDialog.add(panel); openTypeHierarchyTree = new Tree(); openTypeHierarchyTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); openTypeHierarchyTree.setCellRenderer(new TreeNodeRenderer()); openTypeHierarchyTree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { onTypeSelected(); } } }); openTypeHierarchyTree.addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeExpanded(TreeExpansionEvent e) { TreeNode node = (TreeNode)e.getPath().getLastPathComponent(); // Expand node and find the first leaf while (node.getChildCount() > 0) { if (((DefaultMutableTreeNode)node.getChildAt(0)).getUserObject() == null) { // Remove dummy node and create children populateTreeNode(node, null); } if (node.getChildCount() != 1) { break; } node = ((TreeNode)node.getChildAt(0)); } DefaultTreeModel model = (DefaultTreeModel)openTypeHierarchyTree.getModel(); model.reload((TreeNode)e.getPath().getLastPathComponent()); openTypeHierarchyTree.setSelectionPath(new TreePath(node.getPath())); } @Override public void treeCollapsed(TreeExpansionEvent e) {} }); openTypeHierarchyTree.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_F4) { TreeNode node = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent(); if (node != null) { updateTree(node.entry, node.typeName); } } } }); JScrollPane openTypeHierarchyScrollPane = new JScrollPane(openTypeHierarchyTree); openTypeHierarchyScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); openTypeHierarchyScrollPane.setPreferredSize(new Dimension(400, 150)); panel.add(openTypeHierarchyScrollPane, BorderLayout.CENTER); // Buttons "Open" and "Cancel" Box vbox = Box.createVerticalBox(); panel.add(vbox, BorderLayout.SOUTH); vbox.add(Box.createVerticalStrut(25)); Box hbox = Box.createHorizontalBox(); vbox.add(hbox); hbox.add(Box.createHorizontalGlue()); JButton openTypeHierarchyOpenButton = new JButton("Open"); hbox.add(openTypeHierarchyOpenButton); openTypeHierarchyOpenButton.setEnabled(false); openTypeHierarchyOpenButton.addActionListener(e -> onTypeSelected()); hbox.add(Box.createHorizontalStrut(5)); JButton openTypeHierarchyCancelButton = new JButton("Cancel"); hbox.add(openTypeHierarchyCancelButton); Action openTypeHierarchyCancelActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { openTypeHierarchyDialog.setVisible(false); } }; openTypeHierarchyCancelButton.addActionListener(openTypeHierarchyCancelActionListener); openTypeHierarchyTree.addTreeSelectionListener(e -> { Object o = openTypeHierarchyTree.getLastSelectedPathComponent(); if (o != null) { o = ((TreeNode)o).entry; } openTypeHierarchyOpenButton.setEnabled(o != null); }); // Last setup JRootPane rootPane = openTypeHierarchyDialog.getRootPane(); rootPane.setDefaultButton(openTypeHierarchyOpenButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeHierarchyView.cancel"); rootPane.getActionMap().put("OpenTypeHierarchyView.cancel", openTypeHierarchyCancelActionListener); openTypeHierarchyDialog.setMinimumSize(openTypeHierarchyDialog.getSize()); // Prepare to display openTypeHierarchyDialog.pack(); openTypeHierarchyDialog.setLocationRelativeTo(mainFrame); }); } public void show(Collection> collectionOfFutureIndexes, Container.Entry entry, String typeName) { this.collectionOfFutureIndexes = collectionOfFutureIndexes; SwingUtil.invokeLater(() -> { updateTree(entry, typeName); openTypeHierarchyDialog.setVisible(true); openTypeHierarchyTree.requestFocus(); }); } public boolean isVisible() { return openTypeHierarchyDialog.isVisible(); } public void showWaitCursor() { SwingUtil.invokeLater(() -> openTypeHierarchyDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR))); } public void hideWaitCursor() { SwingUtil.invokeLater(() -> openTypeHierarchyDialog.setCursor(Cursor.getDefaultCursor())); } public void updateTree(Collection> collectionOfFutureIndexes) { this.collectionOfFutureIndexes = collectionOfFutureIndexes; TreeNode selectedTreeNode = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent(); if (selectedTreeNode != null) { updateTree(selectedTreeNode.entry, selectedTreeNode.typeName); } } protected void updateTree(Container.Entry entry, String typeName) { SwingUtil.invokeLater(() -> { // Clear tree DefaultTreeModel model = (DefaultTreeModel)openTypeHierarchyTree.getModel(); DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot(); root.removeAllChildren(); TreeNode selectedTreeNode = createTreeNode(entry, typeName); TreeNode parentTreeNode = createParentTreeNode(selectedTreeNode); root.add(parentTreeNode); model.reload(); if (selectedTreeNode != null) { TreePath path = new TreePath(selectedTreeNode.getPath()); // Expand openTypeHierarchyTree.expandPath(path); // Scroll to show tree node openTypeHierarchyTree.makeVisible(path); Rectangle bounds = openTypeHierarchyTree.getPathBounds(path); if(bounds != null) { bounds.x = 0; Rectangle lastRowBounds = openTypeHierarchyTree.getRowBounds(openTypeHierarchyTree.getRowCount()-1); if (lastRowBounds != null) { bounds.y = Math.max(bounds.y-30, 0); bounds.height = Math.min(bounds.height+bounds.y+60, lastRowBounds.height+lastRowBounds.y) - bounds.y; } openTypeHierarchyTree.scrollRectToVisible(bounds); openTypeHierarchyTree.scrollPathToVisible(path); openTypeHierarchyTree.fireVisibleDataPropertyChange(); } // Select tree node openTypeHierarchyTree.setSelectionPath(path); } }); } protected TreeNode createTreeNode(Container.Entry entry, String typeName) { Type type = api.getTypeFactory(entry).make(api, entry, typeName); typeName = type.getName(); List entries = getEntries(typeName); TreeNode treeNode = new TreeNode(entry, typeName, entries, new TreeNodeBean(type)); List childTypeNames = getSubTypeNames(typeName); if (childTypeNames != null) { // Add dummy node treeNode.add(new DefaultMutableTreeNode()); } return treeNode; } /** * Create parent and sibling tree nodes */ protected TreeNode createParentTreeNode(TreeNode treeNode) { Type type = api.getTypeFactory(treeNode.entry).make(api, treeNode.entry, treeNode.typeName); String superTypeName = type.getSuperName(); if (superTypeName != null) { List superEntries = getEntries(superTypeName); // Search entry in the sane container of 'entry' Container.Entry superEntry = null; if ((superEntries != null) && !superEntries.isEmpty()) { for (Container.Entry se : superEntries) { if (se.getContainer() == treeNode.entry.getContainer()) { superEntry = se; break; } } if (superEntry == null) { // Not found -> Choose 1st one superEntry = superEntries.get(0); } } else { superEntry = null; } if (superEntry != null) { // Create parent tree node TreeNode superTreeNode = createTreeNode(superEntry, superTypeName); // Populate parent tree node populateTreeNode(superTreeNode, treeNode); // Recursive call return createParentTreeNode(superTreeNode); } else { // Entry not found --> Most probable hypothesis : Java type entry int lastPackageSeparatorIndex = superTypeName.lastIndexOf('/'); String package_ = superTypeName.substring(0, lastPackageSeparatorIndex).replace('/', '.'); String name = superTypeName.substring(lastPackageSeparatorIndex + 1).replace('$', '.'); String label = (package_ != null) ? name + " - " + package_ : name; Icon icon = ((type.getFlags() & Type.FLAG_INTERFACE) == 0) ? ROOT_CLASS_ICON : ROOT_INTERFACE_ICON; TreeNode rootTreeNode = new TreeNode(null, superTypeName, null, new TreeNodeBean(label, icon)); if (package_.startsWith("java.")) { // If root type is a JDK type, do not create a tree node for each child types rootTreeNode.add(treeNode); } else { populateTreeNode(rootTreeNode, treeNode); } return rootTreeNode; } } else { // super type undefined return treeNode; } } /** * @param superTreeNode node to populate * @param activeTreeNode active child node */ protected void populateTreeNode(TreeNode superTreeNode, TreeNode activeTreeNode) { superTreeNode.removeAllChildren(); // Search preferred container: if 'superTreeNode' is a root with an unknown super entry, uses the container of active child node Container.Entry notNullEntry = superTreeNode.entry; if (notNullEntry == null) { notNullEntry = activeTreeNode.entry; } Container preferredContainer = notNullEntry.getContainer(); String activeTypName = null; if (activeTreeNode != null) { activeTypName = activeTreeNode.typeName; } List subTypeNames = getSubTypeNames(superTreeNode.typeName); ArrayList treeNodes = new ArrayList<>(); for (String subTypeName : subTypeNames) { if (subTypeName.equals(activeTypName)) { treeNodes.add(activeTreeNode); } else { // Search entry in the sane container of 'superTreeNode.entry' List entries = getEntries(subTypeName); Container.Entry entry = null; for (Container.Entry e : entries) { if (e.getContainer() == preferredContainer) { entry = e; } } if (entry == null) { // Not found -> Choose 1st one entry = entries.get(0); } if (entry != null) { // Create type Type t = api.getTypeFactory(entry).make(api, entry, subTypeName); if (t != null) { // Create tree node treeNodes.add(createTreeNode(entry, t.getName())); } } } } treeNodes.sort(TREE_NODE_COMPARATOR); for (TreeNode treeNode : treeNodes) { superTreeNode.add(treeNode); } } public void focus() { SwingUtil.invokeLater(() -> openTypeHierarchyTree.requestFocus()); } protected void onTypeSelected() { TreeNode selectedTreeNode = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent(); if (selectedTreeNode != null) { TreePath path = new TreePath(selectedTreeNode.getPath()); Rectangle bounds = openTypeHierarchyTree.getPathBounds(path); Point listLocation = openTypeHierarchyTree.getLocationOnScreen(); Point leftBottom = new Point(listLocation.x+bounds.x, listLocation.y+bounds.y+bounds.height); selectedTypeCallback.accept(leftBottom, selectedTreeNode.entries, selectedTreeNode.typeName); } } @SuppressWarnings("unchecked") protected List getSubTypeNames(String typeName) { ArrayList result = new ArrayList<>(); try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map subTypeNames = futureIndexes.get().getIndex("subTypeNames"); if (subTypeNames != null) { Collection collection = subTypeNames.get(typeName); if (collection != null) { for (String tn : collection) { if (tn != null) { result.add(tn); } } } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } return result; } @SuppressWarnings("unchecked") protected List getEntries(String typeName) { ArrayList result = new ArrayList<>(); try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map typeDeclarations = futureIndexes.get().getIndex("typeDeclarations"); if (typeDeclarations != null) { Collection collection = typeDeclarations.get(typeName); if (collection != null) { for (Container.Entry e : collection) { if (e != null) { result.add(e); } } } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } return result; } protected static class TreeNode extends DefaultMutableTreeNode { Container.Entry entry; String typeName; List entries; TreeNode(Container.Entry entry, String typeName, List entries, Object userObject) { super(userObject); this.entry = entry; this.typeName = typeName; this.entries = entries; } } // Graphic data for renderer protected static class TreeNodeBean implements TreeNodeData { String label; String tip; Icon icon; Icon openIcon; TreeNodeBean(Type type) { this.label = (type.getDisplayPackageName() != null) ? type.getDisplayTypeName() + " - " + type.getDisplayPackageName() : type.getDisplayTypeName(); this.icon = type.getIcon(); } TreeNodeBean(String label, Icon icon) { this.label = label; this.icon = icon; } @Override public String getLabel() { return label; } @Override public String getTip() { return tip; } @Override public Icon getIcon() { return icon; } @Override public Icon getOpenIcon() { return openIcon; } } protected static class TreeNodeComparator implements Comparator { @Override public int compare(TreeNode tn1, TreeNode tn2) { return ((TreeNodeBean)tn1.getUserObject()).label.compareTo(((TreeNodeBean)tn2.getUserObject()).label); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/OpenTypeView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.function.TriConsumer; import org.jd.gui.util.swing.SwingUtil; import org.jd.gui.view.bean.OpenTypeListCellBean; import org.jd.gui.view.renderer.OpenTypeListCellRenderer; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.function.Consumer; public class OpenTypeView { protected static final int MAX_LINE_COUNT = 80; protected static final TypeNameComparator TYPE_NAME_COMPARATOR = new TypeNameComparator(); protected API api; protected JDialog openTypeDialog; protected JTextField openTypeEnterTextField; protected JLabel openTypeMatchLabel; protected JList openTypeList; @SuppressWarnings("unchecked") public OpenTypeView(API api, JFrame mainFrame, Consumer changedPatternCallback, TriConsumer, String> selectedTypeCallback) { this.api = api; // Build GUI SwingUtil.invokeLater(() -> { openTypeDialog = new JDialog(mainFrame, "Open Type", false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); panel.setLayout(new BorderLayout()); openTypeDialog.add(panel); // Box for "Select a type to open" Box vbox = Box.createVerticalBox(); panel.add(vbox, BorderLayout.NORTH); Box hbox = Box.createHorizontalBox(); hbox.add(new JLabel("Select a type to open (* = any string, ? = any character, TZ = TimeZone):")); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); // Text field vbox.add(openTypeEnterTextField = new JTextField(30)); openTypeEnterTextField.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { switch (e.getKeyChar()) { case '=': case '(': case ')': case '{': case '}': case '[': case ']': e.consume(); break; default: if (Character.isDigit(e.getKeyChar()) && (openTypeEnterTextField.getText().length() == 0)) { // First character can not be a digit e.consume(); } break; } } @Override public void keyPressed(KeyEvent e) { if ((e.getKeyCode() == KeyEvent.VK_DOWN) && (openTypeList.getModel().getSize() > 0)) { openTypeList.setSelectedIndex(0); openTypeList.requestFocus(); e.consume(); } } }); openTypeEnterTextField.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { openTypeList.clearSelection(); } @Override public void focusLost(FocusEvent e) {} }); openTypeEnterTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { call(e); } @Override public void removeUpdate(DocumentEvent e) { call(e); } @Override public void changedUpdate(DocumentEvent e) { call(e); } protected void call(DocumentEvent e) { try { changedPatternCallback.accept(e.getDocument().getText(0, e.getDocument().getLength())); } catch (BadLocationException ex) { assert ExceptionUtil.printStackTrace(ex); } } }); vbox.add(Box.createVerticalStrut(10)); hbox = Box.createHorizontalBox(); hbox.add(openTypeMatchLabel = new JLabel("Matching types:")); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); // List of types JScrollPane scrollPane = new JScrollPane(openTypeList = new JList()); openTypeList.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if ((e.getKeyCode() == KeyEvent.VK_UP) && (openTypeList.getSelectedIndex() == 0)) { openTypeEnterTextField.requestFocus(); e.consume(); } } }); openTypeList.setModel(new DefaultListModel()); openTypeList.setCellRenderer(new OpenTypeListCellRenderer()); openTypeList.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { onTypeSelected(selectedTypeCallback); } } }); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setPreferredSize(new Dimension(400, 150)); panel.add(scrollPane, BorderLayout.CENTER); // Buttons "Open" and "Cancel" vbox = Box.createVerticalBox(); panel.add(vbox, BorderLayout.SOUTH); vbox.add(Box.createVerticalStrut(25)); vbox.add(hbox = Box.createHorizontalBox()); hbox.add(Box.createHorizontalGlue()); JButton openTypeOpenButton = new JButton("Open"); hbox.add(openTypeOpenButton); openTypeOpenButton.setEnabled(false); openTypeOpenButton.addActionListener(e -> onTypeSelected(selectedTypeCallback)); hbox.add(Box.createHorizontalStrut(5)); JButton openTypeCancelButton = new JButton("Cancel"); hbox.add(openTypeCancelButton); Action openTypeCancelActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { openTypeDialog.setVisible(false); } }; openTypeCancelButton.addActionListener(openTypeCancelActionListener); // Last setup JRootPane rootPane = openTypeDialog.getRootPane(); rootPane.setDefaultButton(openTypeOpenButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeView.cancel"); rootPane.getActionMap().put("OpenTypeView.cancel", openTypeCancelActionListener); openTypeList.addListSelectionListener(e -> openTypeOpenButton.setEnabled(openTypeList.getSelectedValue() != null)); openTypeDialog.setMinimumSize(openTypeDialog.getSize()); // Prepare to display openTypeDialog.pack(); openTypeDialog.setLocationRelativeTo(mainFrame); }); } public void show() { SwingUtil.invokeLater(() -> { // Init openTypeEnterTextField.selectAll(); // Show openTypeDialog.setVisible(true); openTypeEnterTextField.requestFocus(); }); } public boolean isVisible() { return openTypeDialog.isVisible(); } public String getPattern() { return openTypeEnterTextField.getText(); } public void showWaitCursor() { SwingUtil.invokeLater(() -> openTypeDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR))); } public void hideWaitCursor() { SwingUtil.invokeLater(() -> openTypeDialog.setCursor(Cursor.getDefaultCursor())); } @SuppressWarnings("unchecked") public void updateList(Map> map) { SwingUtil.invokeLater(() -> { DefaultListModel model = (DefaultListModel)openTypeList.getModel(); ArrayList typeNames = new ArrayList<>(map.keySet()); int index = 0; typeNames.sort(TYPE_NAME_COMPARATOR); model.removeAllElements(); for (String typeName : typeNames) { if (index < MAX_LINE_COUNT) { Collection entries = map.get(typeName); Container.Entry firstEntry = entries.iterator().next(); Type type = api.getTypeFactory(firstEntry).make(api, firstEntry, typeName); if (type != null) { model.addElement(new OpenTypeListCellBean(type.getDisplayTypeName(), type.getDisplayPackageName(), type.getIcon(), entries, typeName)); } else { model.addElement(new OpenTypeListCellBean(typeName, entries, typeName)); } } else if (index == MAX_LINE_COUNT) { model.addElement(null); } } int count = typeNames.size(); switch (count) { case 0: openTypeMatchLabel.setText("Matching types:"); break; case 1: openTypeMatchLabel.setText("1 matching type:"); break; default: openTypeMatchLabel.setText(count + " matching types:"); } }); } public void focus() { SwingUtil.invokeLater(() -> { openTypeList.requestFocus(); }); } protected void onTypeSelected(TriConsumer, String> selectedTypeCallback) { SwingUtil.invokeLater(() -> { int index = openTypeList.getSelectedIndex(); if (index != -1) { OpenTypeListCellBean selectedCellBean = (OpenTypeListCellBean)openTypeList.getModel().getElementAt(index); Point listLocation = openTypeList.getLocationOnScreen(); Rectangle cellBound = openTypeList.getCellBounds(index, index); Point leftBottom = new Point(listLocation.x + cellBound.x, listLocation.y + cellBound.y + cellBound.height); selectedTypeCallback.accept(leftBottom, selectedCellBean.entries, selectedCellBean.typeName); } }); } protected static class TypeNameComparator implements Comparator { @Override public int compare(String tn1, String tn2) { int lasPackageSeparatorIndex = tn1.lastIndexOf('/'); String shortName1 = tn1.substring(lasPackageSeparatorIndex+1); lasPackageSeparatorIndex = tn2.lastIndexOf('/'); String shortName2 = tn2.substring(lasPackageSeparatorIndex+1); return shortName1.compareTo(shortName2); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/PreferencesView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.model.configuration.Configuration; import org.jd.gui.spi.PreferencesPanel; import org.jd.gui.util.swing.SwingUtil; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.*; public class PreferencesView implements PreferencesPanel.PreferencesPanelChangeListener { protected Map preferences; protected Collection panels; protected HashMap valids = new HashMap<>(); protected JDialog preferencesDialog; protected JButton preferencesOkButton = new JButton(); protected Runnable okCallback; public PreferencesView(Configuration configuration, JFrame mainFrame, Collection panels) { this.preferences = configuration.getPreferences(); this.panels = panels; // Build GUI SwingUtil.invokeLater(() -> { preferencesDialog = new JDialog(mainFrame, "Preferences", false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); panel.setLayout(new BorderLayout()); preferencesDialog.add(panel); // Box for preferences panels Box preferencesPanels = Box.createVerticalBox(); preferencesPanels.setBackground(panel.getBackground()); preferencesPanels.setOpaque(true); Color errorBackgroundColor = Color.decode(configuration.getPreferences().get("JdGuiPreferences.errorBackgroundColor")); // Group "PreferencesPanel" by group name HashMap> groups = new HashMap<>(); ArrayList sortedGroupNames = new ArrayList<>(); for (PreferencesPanel pp : panels) { ArrayList pps = groups.get(pp.getPreferencesGroupTitle()); pp.init(errorBackgroundColor); pp.addPreferencesChangeListener(this); if (pps == null) { String groupNames = pp.getPreferencesGroupTitle(); groups.put(groupNames, pps=new ArrayList<>()); sortedGroupNames.add(groupNames); } pps.add(pp); } Collections.sort(sortedGroupNames); // Add preferences panels for (String groupName : sortedGroupNames) { Box vbox = Box.createVerticalBox(); vbox.setBorder(BorderFactory.createTitledBorder(groupName)); ArrayList sortedPreferencesPanels = groups.get(groupName); Collections.sort(sortedPreferencesPanels, new PreferencesPanelComparator()); for (PreferencesPanel pp : sortedPreferencesPanels) { // Add title Box hbox = Box.createHorizontalBox(); JLabel title = new JLabel(pp.getPreferencesPanelTitle()); title.setFont(title.getFont().deriveFont(Font.BOLD)); hbox.add(title); hbox.add(Box.createHorizontalGlue()); hbox.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); vbox.add(hbox); // Add panel JComponent component = pp.getPanel(); component.setMaximumSize(new Dimension(component.getMaximumSize().width, component.getPreferredSize().height)); vbox.add(component); } preferencesPanels.add(vbox); } JScrollPane preferencesScrollPane = new JScrollPane(preferencesPanels); preferencesScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); preferencesScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); panel.add(preferencesScrollPane, BorderLayout.CENTER); Box vbox = Box.createVerticalBox(); panel.add(vbox, BorderLayout.SOUTH); vbox.add(Box.createVerticalStrut(15)); // Buttons "Ok" and "Cancel" Box hbox = Box.createHorizontalBox(); hbox.add(Box.createHorizontalGlue()); preferencesOkButton.setText(" Ok "); preferencesOkButton.addActionListener(e -> { for (PreferencesPanel pp : panels) { pp.savePreferences(preferences); } preferencesDialog.setVisible(false); okCallback.run(); }); hbox.add(preferencesOkButton); hbox.add(Box.createHorizontalStrut(5)); JButton preferencesCancelButton = new JButton("Cancel"); Action preferencesCancelActionListener = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { preferencesDialog.setVisible(false); } }; preferencesCancelButton.addActionListener(preferencesCancelActionListener); hbox.add(preferencesCancelButton); vbox.add(hbox); // Last setup JRootPane rootPane = preferencesDialog.getRootPane(); rootPane.setDefaultButton(preferencesOkButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "PreferencesDescription.cancel"); rootPane.getActionMap().put("PreferencesDescription.cancel", preferencesCancelActionListener); // Size of the screen Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // Height of the task bar Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(preferencesDialog.getGraphicsConfiguration()); // screen height in pixels without taskbar int taskBarHeight = scnMax.bottom + scnMax.top; int maxHeight = screenSize.height - taskBarHeight; int preferredHeight = preferencesPanels.getPreferredSize().height + 2; if (preferredHeight > maxHeight) { preferredHeight = maxHeight; } preferencesScrollPane.setPreferredSize(new Dimension(400, preferredHeight)); preferencesDialog.setMinimumSize(new Dimension(300, 200)); // Prepare to display preferencesDialog.pack(); preferencesDialog.setLocationRelativeTo(mainFrame); }); } public void show(Runnable okCallback) { this.okCallback = okCallback; SwingUtilities.invokeLater(() -> { // Init for (PreferencesPanel pp : panels) { pp.loadPreferences(preferences); } // Show preferencesDialog.setVisible(true); }); } // --- PreferencesPanel.PreferencesChangeListener --- // public void preferencesPanelChanged(PreferencesPanel source) { SwingUtil.invokeLater(() -> { boolean valid = source.arePreferencesValid(); valids.put(source, Boolean.valueOf(valid)); if (valid) { for (PreferencesPanel pp : panels) { if (valids.get(pp) == Boolean.FALSE) { preferencesOkButton.setEnabled(false); return; } } preferencesOkButton.setEnabled(true); } else { preferencesOkButton.setEnabled(false); } }); } protected static class PreferencesPanelComparator implements Comparator { @Override public int compare(PreferencesPanel pp1, PreferencesPanel pp2) { return pp1.getPreferencesPanelTitle().compareTo(pp2.getPreferencesPanelTitle()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.util.swing.SwingUtil; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; public class SaveAllSourcesView { protected JDialog saveAllSourcesDialog; protected JLabel saveAllSourcesLabel; protected JProgressBar saveAllSourcesProgressBar; public SaveAllSourcesView(JFrame mainFrame, Runnable cancelCallback) { // Build GUI SwingUtil.invokeLater(() -> { saveAllSourcesDialog = new JDialog(mainFrame, "Save All Sources", false); saveAllSourcesDialog.setResizable(false); saveAllSourcesDialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { cancelCallback.run(); } }); Box vbox = Box.createVerticalBox(); vbox.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); saveAllSourcesDialog.add(vbox); // First label "Saving 'file' ..." Box hbox = Box.createHorizontalBox(); hbox.add(saveAllSourcesLabel = new JLabel()); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); vbox.add(saveAllSourcesProgressBar = new JProgressBar()); vbox.add(Box.createVerticalStrut(15)); // Button "Cancel" hbox = Box.createHorizontalBox(); hbox.add(Box.createHorizontalGlue()); JButton saveAllSourcesCancelButton = new JButton("Cancel"); Action saveAllSourcesCancelActionListener = new AbstractAction() { public void actionPerformed(ActionEvent actionEvent) { cancelCallback.run(); saveAllSourcesDialog.setVisible(false); } }; saveAllSourcesCancelButton.addActionListener(saveAllSourcesCancelActionListener); hbox.add(saveAllSourcesCancelButton); vbox.add(hbox); // Last setup JRootPane rootPane = saveAllSourcesDialog.getRootPane(); rootPane.setDefaultButton(saveAllSourcesCancelButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "SaveAllSourcesView.cancel"); rootPane.getActionMap().put("SaveAllSourcesView.cancel", saveAllSourcesCancelActionListener); // Prepare to display saveAllSourcesDialog.pack(); }); } public void show(File file) { SwingUtil.invokeLater(() -> { // Init saveAllSourcesLabel.setText("Saving '" + file.getAbsolutePath() + "'..."); saveAllSourcesProgressBar.setValue(0); saveAllSourcesProgressBar.setMaximum(10); saveAllSourcesProgressBar.setIndeterminate(true); saveAllSourcesDialog.pack(); // Show saveAllSourcesDialog.setLocationRelativeTo(saveAllSourcesDialog.getParent()); saveAllSourcesDialog.setVisible(true); }); } public boolean isVisible() { return saveAllSourcesDialog.isVisible(); } public void setMaxValue(int maxValue) { SwingUtil.invokeLater(() -> { if (maxValue > 0) { saveAllSourcesProgressBar.setMaximum(maxValue); saveAllSourcesProgressBar.setIndeterminate(false); } else { saveAllSourcesProgressBar.setIndeterminate(true); } }); } public void updateProgressBar(int value) { SwingUtil.invokeLater(() -> { saveAllSourcesProgressBar.setValue(value); }); } public void hide() { SwingUtil.invokeLater(() -> { saveAllSourcesDialog.setVisible(false); }); } public void showActionFailedDialog() { SwingUtil.invokeLater(() -> { JOptionPane.showMessageDialog(saveAllSourcesDialog, "'Save All Sources' action failed.", "Error", JOptionPane.ERROR_MESSAGE); }); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.TreeNodeExpandable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.DelegatingFilterContainer; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.util.function.TriConsumer; import org.jd.gui.util.swing.SwingUtil; import org.jd.gui.view.component.Tree; import org.jd.gui.view.renderer.TreeNodeRenderer; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.*; import java.net.URI; import java.util.*; import java.util.function.BiConsumer; public class SearchInConstantPoolsView { protected static final ContainerComparator CONTAINER_COMPARATOR = new ContainerComparator(); public static final int SEARCH_TYPE = 1; public static final int SEARCH_CONSTRUCTOR = 2; public static final int SEARCH_METHOD = 4; public static final int SEARCH_FIELD = 8; public static final int SEARCH_STRING = 16; public static final int SEARCH_MODULE = 32; public static final int SEARCH_DECLARATION = 64; public static final int SEARCH_REFERENCE = 128; protected API api; protected Set accepted = new HashSet<>(); protected Set expanded = new HashSet<>(); protected JDialog searchInConstantPoolsDialog; protected JTextField searchInConstantPoolsEnterTextField; protected JLabel searchInConstantPoolsLabel; protected JCheckBox searchInConstantPoolsCheckBoxType; protected JCheckBox searchInConstantPoolsCheckBoxField; protected JCheckBox searchInConstantPoolsCheckBoxConstructor; protected JCheckBox searchInConstantPoolsCheckBoxMethod; protected JCheckBox searchInConstantPoolsCheckBoxString; protected JCheckBox searchInConstantPoolsCheckBoxModule; protected JCheckBox searchInConstantPoolsCheckBoxDeclarations; protected JCheckBox searchInConstantPoolsCheckBoxReferences; protected Tree searchInConstantPoolsTree; @SuppressWarnings("unchecked") public SearchInConstantPoolsView( API api, JFrame mainFrame, BiConsumer changedPatternCallback, TriConsumer selectedTypeCallback) { this.api = api; // Build GUI SwingUtil.invokeLater(() -> { searchInConstantPoolsDialog = new JDialog(mainFrame, "Search", false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); panel.setLayout(new BorderLayout()); searchInConstantPoolsDialog.add(panel); // Box for search criteria Box vbox = Box.createVerticalBox(); Box hbox = Box.createHorizontalBox(); hbox.add(new JLabel("Search string (* = any string, ? = any character):")); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); // Text field vbox.add(searchInConstantPoolsEnterTextField = new JTextField(30)); searchInConstantPoolsEnterTextField.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { switch (e.getKeyChar()) { case '=': case '(': case ')': case '{': case '}': case '[': case ']': e.consume(); break; default: if (Character.isDigit(e.getKeyChar()) && (searchInConstantPoolsEnterTextField.getText().length() == 0)) { // First character can not be a digit e.consume(); } break; } } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_DOWN) { DefaultMutableTreeNode root = (DefaultMutableTreeNode)searchInConstantPoolsTree.getModel().getRoot(); if (root.getChildCount() > 0) { searchInConstantPoolsTree.requestFocus(); if (searchInConstantPoolsTree.getSelectionCount() == 0) { searchInConstantPoolsTree.setSelectionPath(new TreePath(((DefaultMutableTreeNode)root.getChildAt(0)).getPath())); } e.consume(); } } } }); searchInConstantPoolsEnterTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { call(); } @Override public void removeUpdate(DocumentEvent e) { call(); } @Override public void changedUpdate(DocumentEvent e) { call(); } protected void call() { changedPatternCallback.accept(searchInConstantPoolsEnterTextField.getText(), getFlags()); } }); vbox.add(Box.createVerticalStrut(10)); hbox = Box.createHorizontalBox(); vbox.add(hbox); JPanel subpanel = new JPanel(); subpanel.setBorder(BorderFactory.createTitledBorder("Search For")); subpanel.setLayout(new BorderLayout()); hbox.add(subpanel); Box subhbox = Box.createHorizontalBox(); subpanel.add(subhbox, BorderLayout.WEST); ItemListener checkBoxListener = (e) -> { changedPatternCallback.accept(searchInConstantPoolsEnterTextField.getText(), getFlags()); searchInConstantPoolsEnterTextField.requestFocus(); }; JPanel subsubpanel = new JPanel(); subsubpanel.setLayout(new GridLayout(2, 1)); subsubpanel.add(searchInConstantPoolsCheckBoxType = new JCheckBox("Type", true)); searchInConstantPoolsCheckBoxType.addItemListener(checkBoxListener); subsubpanel.add(searchInConstantPoolsCheckBoxField = new JCheckBox("Field")); searchInConstantPoolsCheckBoxField.addItemListener(checkBoxListener); subhbox.add(subsubpanel); subsubpanel = new JPanel(); subsubpanel.setLayout(new GridLayout(2, 1)); subsubpanel.add(searchInConstantPoolsCheckBoxConstructor = new JCheckBox("Constructor")); searchInConstantPoolsCheckBoxConstructor.addItemListener(checkBoxListener); subsubpanel.add(searchInConstantPoolsCheckBoxMethod = new JCheckBox("Method")); searchInConstantPoolsCheckBoxMethod.addItemListener(checkBoxListener); subhbox.add(subsubpanel); subsubpanel = new JPanel(); subsubpanel.setLayout(new GridLayout(2, 1)); subsubpanel.add(searchInConstantPoolsCheckBoxString = new JCheckBox("String Constant")); searchInConstantPoolsCheckBoxString.addItemListener(checkBoxListener); subsubpanel.add(searchInConstantPoolsCheckBoxModule = new JCheckBox("Java Module")); searchInConstantPoolsCheckBoxModule.addItemListener(checkBoxListener); subhbox.add(subsubpanel); subpanel = new JPanel(); subpanel.setBorder(BorderFactory.createTitledBorder("Limit To")); subpanel.setLayout(new BorderLayout()); hbox.add(subpanel); subhbox = Box.createHorizontalBox(); subpanel.add(subhbox, BorderLayout.WEST); subsubpanel = new JPanel(); subsubpanel.setLayout(new GridLayout(2, 1)); subsubpanel.add(searchInConstantPoolsCheckBoxDeclarations = new JCheckBox("Declarations", true)); searchInConstantPoolsCheckBoxDeclarations.addItemListener(checkBoxListener); subsubpanel.add(searchInConstantPoolsCheckBoxReferences = new JCheckBox("References", true)); searchInConstantPoolsCheckBoxReferences.addItemListener(checkBoxListener); subhbox.add(subsubpanel); vbox.add(Box.createVerticalStrut(10)); hbox = Box.createHorizontalBox(); hbox.add(searchInConstantPoolsLabel = new JLabel("Matching types:")); hbox.add(Box.createHorizontalGlue()); vbox.add(hbox); vbox.add(Box.createVerticalStrut(10)); panel.add(vbox, BorderLayout.NORTH); JScrollPane scrollPane = new JScrollPane(searchInConstantPoolsTree = new Tree()); searchInConstantPoolsTree.setShowsRootHandles(true); searchInConstantPoolsTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); searchInConstantPoolsTree.setCellRenderer(new TreeNodeRenderer()); searchInConstantPoolsTree.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_UP) { if (searchInConstantPoolsTree.getLeadSelectionRow() == 0) { searchInConstantPoolsEnterTextField.requestFocus(); e.consume(); } } } }); searchInConstantPoolsTree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { T node = (T)searchInConstantPoolsTree.getLastSelectedPathComponent(); if (node != null) { selectedTypeCallback.accept(node.getUri(), searchInConstantPoolsEnterTextField.getText(), getFlags()); } } } }); searchInConstantPoolsTree.addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeExpanded(TreeExpansionEvent e) { DefaultTreeModel model = (DefaultTreeModel)searchInConstantPoolsTree.getModel(); T node = (T)e.getPath().getLastPathComponent(); // Expand node and find the first leaf while (true) { populate(model, node); if (node.getChildCount() == 0) { break; } node = (T)node.getChildAt(0); } searchInConstantPoolsTree.setSelectionPath(new TreePath(node.getPath())); } @Override public void treeCollapsed(TreeExpansionEvent e) {} }); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setPreferredSize(new Dimension(400, 150)); panel.add(scrollPane, BorderLayout.CENTER); vbox = Box.createVerticalBox(); vbox.add(Box.createVerticalStrut(25)); hbox = Box.createHorizontalBox(); hbox.add(Box.createHorizontalGlue()); JButton searchInConstantPoolsOpenButton = new JButton("Open"); hbox.add(searchInConstantPoolsOpenButton); searchInConstantPoolsOpenButton.setEnabled(false); Action searchInConstantPoolsOpenActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { T selectedTreeNode = (T)searchInConstantPoolsTree.getLastSelectedPathComponent(); if (selectedTreeNode != null) { selectedTypeCallback.accept(selectedTreeNode.getUri(), searchInConstantPoolsEnterTextField.getText(), getFlags()); } } }; searchInConstantPoolsOpenButton.addActionListener(searchInConstantPoolsOpenActionListener); hbox.add(Box.createHorizontalStrut(5)); JButton searchInConstantPoolsCancelButton = new JButton("Cancel"); hbox.add(searchInConstantPoolsCancelButton); Action searchInConstantPoolsCancelActionListener = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { searchInConstantPoolsDialog.setVisible(false); } }; searchInConstantPoolsCancelButton.addActionListener(searchInConstantPoolsCancelActionListener); vbox.add(hbox); panel.add(vbox, BorderLayout.SOUTH); // Last setup JRootPane rootPane = searchInConstantPoolsDialog.getRootPane(); rootPane.setDefaultButton(searchInConstantPoolsOpenButton); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "SearchInConstantPoolsView.cancel"); rootPane.getActionMap().put("SearchInConstantPoolsView.cancel", searchInConstantPoolsCancelActionListener); searchInConstantPoolsDialog.setMinimumSize(searchInConstantPoolsDialog.getSize()); searchInConstantPoolsEnterTextField.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { searchInConstantPoolsTree.clearSelection(); searchInConstantPoolsOpenButton.setEnabled(false); } }); searchInConstantPoolsTree.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { searchInConstantPoolsOpenButton.setEnabled(searchInConstantPoolsTree.getSelectionCount() > 0); } }); // Prepare to display searchInConstantPoolsDialog.pack(); searchInConstantPoolsDialog.setLocationRelativeTo(searchInConstantPoolsDialog.getParent()); }); } @SuppressWarnings("unchecked") protected void populate(DefaultTreeModel model, T node) { // Populate node populate(node); // Populate children int i = node.getChildCount(); while (i-- > 0) { T child = (T)node.getChildAt(i); if ((child instanceof TreeNodeExpandable) && !expanded.contains(child.getUri())) { populate(child); } } // Refresh model.reload(node); } @SuppressWarnings("unchecked") protected void populate(T node) { if ((node instanceof TreeNodeExpandable) && !expanded.contains(node.getUri())) { // Populate ((TreeNodeExpandable)node).populateTreeNode(api); expanded.add(node.getUri()); // Filter int i = node.getChildCount(); while (i-- > 0) { if (!accepted.contains(((T)node.getChildAt(i)).getUri())) { node.remove(i); } } } } public void show() { SwingUtil.invokeLater(() -> { searchInConstantPoolsEnterTextField.selectAll(); // Show searchInConstantPoolsDialog.setVisible(true); searchInConstantPoolsEnterTextField.requestFocus(); }); } public boolean isVisible() { return searchInConstantPoolsDialog.isVisible(); } public String getPattern() { return searchInConstantPoolsEnterTextField.getText(); } public int getFlags() { int flags = 0; if (searchInConstantPoolsCheckBoxType.isSelected()) flags += SEARCH_TYPE; if (searchInConstantPoolsCheckBoxConstructor.isSelected()) flags += SEARCH_CONSTRUCTOR; if (searchInConstantPoolsCheckBoxMethod.isSelected()) flags += SEARCH_METHOD; if (searchInConstantPoolsCheckBoxField.isSelected()) flags += SEARCH_FIELD; if (searchInConstantPoolsCheckBoxString.isSelected()) flags += SEARCH_STRING; if (searchInConstantPoolsCheckBoxModule.isSelected()) flags += SEARCH_MODULE; if (searchInConstantPoolsCheckBoxDeclarations.isSelected()) flags += SEARCH_DECLARATION; if (searchInConstantPoolsCheckBoxReferences.isSelected()) flags += SEARCH_REFERENCE; return flags; } public void showWaitCursor() { SwingUtil.invokeLater(() -> searchInConstantPoolsDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR))); } public void hideWaitCursor() { SwingUtil.invokeLater(() -> searchInConstantPoolsDialog.setCursor(Cursor.getDefaultCursor())); } @SuppressWarnings("unchecked") public void updateTree(Collection containers, int matchingTypeCount) { SwingUtil.invokeLater(() -> { DefaultTreeModel model = (DefaultTreeModel)searchInConstantPoolsTree.getModel(); T root = (T)model.getRoot(); // Reset tree nodes root.removeAllChildren(); accepted.clear(); expanded.clear(); if (containers != null) { ArrayList list = new ArrayList<>(containers); list.sort(CONTAINER_COMPARATOR); for (DelegatingFilterContainer container : list) { // Init uri set accepted.addAll(container.getUris()); // Populate tree Container.Entry parentEntry = container.getRoot().getParent(); TreeNodeFactory treeNodeFactory = api.getTreeNodeFactory(parentEntry); if (treeNodeFactory != null) { root.add(treeNodeFactory.make(api, parentEntry)); } } // Expand node and find the first leaf T node = root; while (true) { populate(model, node); if (node.getChildCount() == 0) { break; } node = (T)node.getChildAt(0); } searchInConstantPoolsTree.setSelectionPath(new TreePath(node.getPath())); } else { model.reload(); } // Update matching item counter switch (matchingTypeCount) { case 0: searchInConstantPoolsLabel.setText("Matching entries:"); break; case 1: searchInConstantPoolsLabel.setText("1 matching entry:"); break; default: searchInConstantPoolsLabel.setText(matchingTypeCount + " matching entries:"); } }); } protected static class ContainerComparator implements Comparator { @Override public int compare(Container c1, Container c2) { return c1.getRoot().getUri().compareTo(c2.getRoot().getUri()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/SelectLocationView.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.TreeNodeExpandable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.DelegatingFilterContainer; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.util.swing.SwingUtil; import org.jd.gui.view.component.Tree; import org.jd.gui.view.renderer.TreeNodeRenderer; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.*; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Set; import java.util.function.Consumer; public class SelectLocationView { protected static final DelegatingFilterContainerComparator DELEGATING_FILTER_CONTAINER_COMPARATOR = new DelegatingFilterContainerComparator(); protected API api; protected JDialog selectLocationDialog; protected JLabel selectLocationLabel; protected Tree selectLocationTree; protected Consumer selectedEntryCallback; protected Runnable closeCallback; @SuppressWarnings("unchecked") public SelectLocationView(API api, JFrame mainFrame) { this.api = api; // Build GUI SwingUtil.invokeLater(() -> { selectLocationDialog = new JDialog(mainFrame, "", false); selectLocationDialog.setUndecorated(true); selectLocationDialog.addWindowListener(new WindowAdapter() { @Override public void windowDeactivated(WindowEvent e) { closeCallback.run(); } }); Color bg = UIManager.getColor("ToolTip.background"); JPanel selectLocationPanel = new JPanel(new BorderLayout()); selectLocationPanel.setBorder(BorderFactory.createLineBorder(bg.darker())); selectLocationPanel.setBackground(bg); selectLocationDialog.add(selectLocationPanel); selectLocationLabel = new JLabel(); selectLocationLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); selectLocationPanel.add(selectLocationLabel, BorderLayout.NORTH); selectLocationTree = new Tree(); selectLocationTree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); selectLocationTree.setOpaque(false); selectLocationTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); selectLocationTree.setCellRenderer(new TreeNodeRenderer()); selectLocationTree.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { onSelectedEntry(); } } }); selectLocationTree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() > 0) { onSelectedEntry(); } } }); selectLocationTree.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { selectLocationDialog.setVisible(false); } }); selectLocationPanel.add(selectLocationTree, BorderLayout.CENTER); // Last setup JRootPane rootPane = selectLocationDialog.getRootPane(); rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "SelectLocationView.cancel"); rootPane.getActionMap().put("SelectLocationView.cancel", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { selectLocationDialog.setVisible(false); } }); }); } @SuppressWarnings("unchecked") public void show(Point location, Collection containers, int locationCount, Consumer selectedEntryCallback, Runnable closeCallback) { this.selectedEntryCallback = selectedEntryCallback; this.closeCallback = closeCallback; SwingUtil.invokeLater(() -> { // Init T root = (T)selectLocationTree.getModel().getRoot(); // Reset tree nodes root.removeAllChildren(); ArrayList sortedContainers = new ArrayList<>(containers); sortedContainers.sort(DELEGATING_FILTER_CONTAINER_COMPARATOR); for (DelegatingFilterContainer container : sortedContainers) { Container.Entry parentEntry = container.getRoot().getParent(); TreeNodeFactory factory = api.getTreeNodeFactory(parentEntry); if (factory != null) { T node = factory.make(api, parentEntry); if (node != null) { root.add(node); populate(container.getUris(), node); } } } ((DefaultTreeModel)selectLocationTree.getModel()).reload(); // Expand all nodes for (int row = 0; row < selectLocationTree.getRowCount(); row++) { selectLocationTree.expandRow(row); } // Select first leaf T node = root; while (true) { if (node.getChildCount() == 0) { break; } node = (T)node.getChildAt(0); } selectLocationTree.setSelectionPath(new TreePath(node.getPath())); // Reset preferred size selectLocationTree.setPreferredSize(null); // Resize Dimension ps = selectLocationTree.getPreferredSize(); if (ps.width < 200) ps.width = 200; if (ps.height < 50) ps.height = 50; selectLocationTree.setPreferredSize(ps); selectLocationLabel.setText("" + locationCount + " locations:"); selectLocationDialog.pack(); selectLocationDialog.setLocation(location); // Show selectLocationDialog.setVisible(true); selectLocationTree.requestFocus(); }); } @SuppressWarnings("unchecked") protected void populate(Set uris, DefaultMutableTreeNode node) { if (node instanceof TreeNodeExpandable) { ((TreeNodeExpandable)node).populateTreeNode(api); int i = node.getChildCount(); while (i-- > 0) { T child = (T)node.getChildAt(i); if (uris.contains(child.getUri())) { populate(uris, child); } else { node.remove(i); } } } } @SuppressWarnings("unchecked") protected void onSelectedEntry() { T node = (T)selectLocationTree.getLastSelectedPathComponent(); if (node != null) { selectLocationDialog.setVisible(false); selectedEntryCallback.accept(node.getUri()); } } protected static class DelegatingFilterContainerComparator implements Comparator { @Override public int compare(DelegatingFilterContainer fcw1, DelegatingFilterContainer fcw2) { return fcw1.getRoot().getUri().compareTo(fcw2.getRoot().getUri()); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.bean; import org.jd.gui.api.model.Container; import javax.swing.*; import java.util.Collection; public class OpenTypeListCellBean { public String label; public String packag; public Icon icon; public Collection entries; public String typeName; public OpenTypeListCellBean(String label, Collection entries, String typeName) { this.label = label; this.entries = entries; this.typeName = typeName; } public OpenTypeListCellBean(String label, String packag, Icon icon, Collection entries, String typeName) { this.label = label; this.packag = packag; this.icon = icon; this.entries = entries; this.typeName = typeName; } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/IconButton.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import javax.swing.*; import java.awt.*; public class IconButton extends JButton { protected static final Insets INSETS0 = new Insets(0, 0, 0, 0); public IconButton(String text, Action action) { setFocusPainted(false); setBorderPainted(false); setMargin(INSETS0); setAction(action); setText(text); } public IconButton(Action action) { this(null, action); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/List.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.jd.gui.api.model.TreeNodeData; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.awt.*; import java.awt.event.KeyEvent; public class List extends JList { @SuppressWarnings("unchecked") public List() { super(); Toolkit toolkit = Toolkit.getDefaultToolkit(); KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, toolkit.getMenuShortcutKeyMask()); KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, toolkit.getMenuShortcutKeyMask()); KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, toolkit.getMenuShortcutKeyMask()); InputMap inputMap = getInputMap(); inputMap.put(ctrlA, "none"); inputMap.put(ctrlC, "none"); inputMap.put(ctrlV, "none"); setCellRenderer(new Renderer()); } protected class Renderer implements ListCellRenderer { protected Color textSelectionColor; protected Color backgroundSelectionColor; protected Color textNonSelectionColor; protected Color backgroundNonSelectionColor; protected JLabel label; public Renderer() { label = new JLabel(); label.setOpaque(true); textSelectionColor = UIManager.getColor("List.dropCellForeground"); backgroundSelectionColor = UIManager.getColor("List.dropCellBackground"); textNonSelectionColor = UIManager.getColor("List.foreground"); backgroundNonSelectionColor = UIManager.getColor("List.background"); Insets margins = UIManager.getInsets("List.contentMargins"); if (textSelectionColor == null) textSelectionColor = List.this.getSelectionForeground(); if (backgroundSelectionColor == null) backgroundSelectionColor = List.this.getSelectionBackground(); if (margins != null) { label.setBorder(BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right)); } else { label.setBorder(BorderFactory.createEmptyBorder(0, 2, 1, 2)); } } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) { Object data = ((DefaultMutableTreeNode)value).getUserObject(); if (data instanceof TreeNodeData) { TreeNodeData tnd = (TreeNodeData)data; label.setIcon(tnd.getIcon()); label.setText(tnd.getLabel()); } else { label.setIcon(null); label.setText("" + data); } if (selected) { label.setForeground(textSelectionColor); label.setBackground(backgroundSelectionColor); } else { label.setForeground(textNonSelectionColor); label.setBackground(backgroundNonSelectionColor); } return label; } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/Tree.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; public class Tree extends JTree { public Tree() { Toolkit toolkit = Toolkit.getDefaultToolkit(); KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, toolkit.getMenuShortcutKeyMask()); KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, toolkit.getMenuShortcutKeyMask()); KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, toolkit.getMenuShortcutKeyMask()); InputMap inputMap = getInputMap(); inputMap.put(ctrlA, "none"); inputMap.put(ctrlC, "none"); inputMap.put(ctrlV, "none"); setRootVisible(false); } public void fireVisibleDataPropertyChange() { if (getAccessibleContext() != null) { getAccessibleContext().firePropertyChange("AccessibleVisibleData", false, true); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component.panel; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.service.platform.PlatformService; import javax.swing.*; import java.awt.*; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; @SuppressWarnings("unchecked") public class MainTabbedPanel extends TabbedPanel implements UriOpenable, PreferencesChangeListener, PageChangeListener { protected ArrayList pageChangedListeners = new ArrayList<>(); // Flag to prevent the event cascades protected boolean pageChangedListenersEnabled = true; public MainTabbedPanel(API api) { super(api); } @Override public void create() { setLayout(cardLayout = new CardLayout()); Color bg = darker(getBackground()); if (PlatformService.getInstance().isWindows()) { setBackground(bg); } // panel // JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.setBackground(bg); Color fontColor = panel.getBackground().darker(); panel.add(Box.createHorizontalGlue()); JPanel box = new JPanel(); box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); box.setBackground(panel.getBackground()); box.add(Box.createVerticalGlue()); JLabel title = newLabel("No files are open", fontColor); title.setFont(title.getFont().deriveFont(Font.BOLD, title.getFont().getSize()+8)); box.add(title); box.add(newLabel("Open a file with menu \"File > Open File...\"", fontColor)); box.add(newLabel("Open recent files with menu \"File > Recent Files\"", fontColor)); box.add(newLabel("Drag and drop files from " + getFileManagerLabel(), fontColor)); box.add(Box.createVerticalGlue()); panel.add(box); panel.add(Box.createHorizontalGlue()); add("panel", panel); // tabs // tabbedPane = createTabPanel(); tabbedPane.addChangeListener(e -> { if (pageChangedListenersEnabled) { JComponent subPage = (JComponent)tabbedPane.getSelectedComponent(); if (subPage == null) { // Fire page changed event for (PageChangeListener listener : pageChangedListeners) { listener.pageChanged(null); } } else { T page = (T)subPage.getClientProperty("currentPage"); if (page == null) { page = (T)tabbedPane.getSelectedComponent(); } // Fire page changed event for (PageChangeListener listener : pageChangedListeners) { listener.pageChanged(page); } // Update current sub-page preferences if (subPage instanceof PreferencesChangeListener) { ((PreferencesChangeListener)subPage).preferencesChanged(preferences); } } } }); add("tabs", tabbedPane); setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, darker(darker(bg)))); } protected String getFileManagerLabel() { switch (PlatformService.getInstance().getOs()) { case Linux: return "your file manager"; case MacOSX: return "the Finder"; default: return "Explorer"; } } protected JLabel newLabel(String text, Color fontColor) { JLabel label = new JLabel(text); label.setForeground(fontColor); return label; } @Override public void addPage(String title, Icon icon, String tip, T page) { super.addPage(title, icon, tip, page); if (page instanceof PageChangeable) { ((PageChangeable)page).addPageChangeListener(this); } } public List getPages() { int i = tabbedPane.getTabCount(); ArrayList pages = new ArrayList<>(i); while (i-- > 0) { pages.add((T)tabbedPane.getComponentAt(i)); } return pages; } public ArrayList getPageChangedListeners() { return pageChangedListeners; } // --- URIOpener --- // @Override public boolean openUri(URI uri) { try { // Disable page changed event pageChangedListenersEnabled = false; // Search & display main tab T page = showPage(uri); if (page != null) { if (page instanceof UriOpenable) { // Enable page changed event pageChangedListenersEnabled = true; // Search & display sub tab return ((UriOpenable)page).openUri(uri); } return true; } } finally { // Enable page changed event pageChangedListenersEnabled = true; } return false; } // --- PageChangedListener --- // @Override public void pageChanged(T page) { // Store active page for current sub tabbed pane Component subPage = tabbedPane.getSelectedComponent(); if (subPage != null) { ((JComponent)subPage).putClientProperty("currentPage", page); } if (page == null) { page = (T)subPage; } // Forward event for (PageChangeListener listener : pageChangedListeners) { listener.pageChanged(page); } } // --- PreferencesChangeListener --- // @Override public void preferencesChanged(Map preferences) { super.preferencesChanged(preferences); // Update current sub-page preferences Component subPage = tabbedPane.getSelectedComponent(); if (subPage instanceof PreferencesChangeListener) { ((PreferencesChangeListener)subPage).preferencesChanged(preferences); } } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component.panel; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PreferencesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.service.platform.PlatformService; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.*; import java.net.URI; import java.util.Collection; import java.util.Map; public class TabbedPanel extends JPanel implements PreferencesChangeListener { protected static final ImageIcon CLOSE_ICON = new ImageIcon(TabbedPanel.class.getClassLoader().getResource("org/jd/gui/images/close.gif")); protected static final ImageIcon CLOSE_ACTIVE_ICON = new ImageIcon(TabbedPanel.class.getClassLoader().getResource("org/jd/gui/images/close_active.gif")); protected static final String TAB_LAYOUT = "UITabsPreferencesProvider.singleLineTabs"; protected API api; protected CardLayout cardLayout; protected JTabbedPane tabbedPane; protected Map preferences; public TabbedPanel(API api) { this.api = api; create(); } protected void create() { setLayout(cardLayout = new CardLayout()); add("panel", new JPanel()); add("tabs", tabbedPane = createTabPanel()); } protected JTabbedPane createTabPanel() { JTabbedPane tabPanel = new JTabbedPane() { @Override public String getToolTipText(MouseEvent e) { int index = indexAtLocation(e.getX(), e.getY()); if (index != -1) { return ((JComponent)getTabComponentAt(index)).getToolTipText(); } return super.getToolTipText(e); } }; ToolTipManager.sharedInstance().registerComponent(tabPanel); tabPanel.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { showPopupTabMenu(e); } @Override public void mouseReleased(MouseEvent e) { showPopupTabMenu(e); } protected void showPopupTabMenu(MouseEvent e) { if (e.isPopupTrigger()) { int index = tabPanel.indexAtLocation(e.getX(), e.getY()); if (index != -1) { new PopupTabMenu(tabPanel.getComponentAt(index)).show(e.getComponent(), e.getX(), e.getY()); } } } }); return tabPanel; } protected static Color darker(Color c) { return new Color( Math.max((int)(c.getRed() *0.85), 0), Math.max((int)(c.getGreen()*0.85), 0), Math.max((int)(c.getBlue() *0.85), 0), c.getAlpha()); } public void addPage(String title, Icon icon, String tip, T page) { // Add a new tab JLabel tabCloseButton = new JLabel(CLOSE_ICON); tabCloseButton.setToolTipText("Close this panel"); tabCloseButton.addMouseListener(new MouseListener() { @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) { ((JLabel)e.getSource()).setIcon(CLOSE_ACTIVE_ICON); } @Override public void mouseExited(MouseEvent e) { ((JLabel)e.getSource()).setIcon(CLOSE_ICON); } @Override public void mouseClicked(MouseEvent e) { removeComponent(page); } }); JPanel tab = new JPanel(new BorderLayout()); tab.setBorder(BorderFactory.createEmptyBorder(2, 0, 3, 0)); tab.setOpaque(false); tab.setToolTipText(tip); tab.add(new JLabel(title, icon, JLabel.LEADING), BorderLayout.CENTER); tab.add(tabCloseButton, BorderLayout.EAST); ToolTipManager.sharedInstance().unregisterComponent(tab); int index = tabbedPane.getTabCount(); tabbedPane.addTab(title, page); tabbedPane.setTabComponentAt(index, tab); setSelectedIndex(index); cardLayout.show(this, "tabs"); } protected void setSelectedIndex(int index) { if (index != -1) { if (tabbedPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { // Ensure that the new page is visible (bug with SCROLL_TAB_LAYOUT) ChangeEvent event = new ChangeEvent(tabbedPane); for (ChangeListener listener : tabbedPane.getChangeListeners()) { if (listener.getClass().getPackage().getName().startsWith("javax.")) { listener.stateChanged(event); } } } tabbedPane.setSelectedIndex(index); } } @SuppressWarnings("unchecked") protected T showPage(URI uri) { String u1 = uri.getPath(); int i = tabbedPane.getTabCount(); while (i-- > 0) { T page = (T)tabbedPane.getComponentAt(i); String u2 = page.getUri().getPath(); if (u1.startsWith(u2)) { tabbedPane.setSelectedIndex(i); return page; } } return null; } protected class PopupTabMenu extends JPopupMenu { public PopupTabMenu(Component component) { // Add default popup menu entries JMenuItem menuItem = new JMenuItem("Close", null); menuItem.addActionListener(e -> removeComponent(component)); add(menuItem); menuItem = new JMenuItem("Close Others", null); menuItem.addActionListener(e -> removeOtherComponents(component)); add(menuItem); menuItem = new JMenuItem("Close All", null); menuItem.addActionListener(e -> removeAllComponents()); add(menuItem); // Add "Select Tab" popup menu entry if ((tabbedPane.getTabCount() > 1) && (PlatformService.getInstance().isMac() || "true".equals(preferences.get(TAB_LAYOUT)))) { addSeparator(); JMenu menu = new JMenu("Select Tab"); int count = tabbedPane.getTabCount(); for (int i = 0; i < count; i++) { JPanel tab = (JPanel) tabbedPane.getTabComponentAt(i); JLabel label = (JLabel) tab.getComponent(0); JMenuItem subMenuItem = new JMenuItem(label.getText(), label.getIcon()); subMenuItem.addActionListener(new SubMenuItemActionListener(i)); if (component == tabbedPane.getComponentAt(i)) { subMenuItem.setFont(subMenuItem.getFont().deriveFont(Font.BOLD)); } menu.add(subMenuItem); } add(menu); } // Add SPI popup menu entries if (component instanceof ContainerEntryGettable) { Collection actions = api.getContextualActions(((ContainerEntryGettable)component).getEntry(), null); if (actions != null) { addSeparator(); for (Action action : actions) { if (action != null) { add(action); } else { addSeparator(); } } } } } } public JTabbedPane getTabbedPane() { return tabbedPane; } protected class SubMenuItemActionListener implements ActionListener { protected int index; public SubMenuItemActionListener(int index) { this.index = index; } @Override public void actionPerformed(ActionEvent e) { tabbedPane.setSelectedIndex(index); } } // --- Popup menu actions --- // public void removeComponent(Component component) { tabbedPane.remove(component); if (tabbedPane.getTabCount() == 0) { cardLayout.show(this, "panel"); } } protected void removeOtherComponents(Component component) { int i = tabbedPane.getTabCount(); while (i-- > 0) { Component c = tabbedPane.getComponentAt(i); if (c != component) { tabbedPane.remove(i); } } if (tabbedPane.getTabCount() == 0) { cardLayout.show(this, "panel"); } } protected void removeAllComponents() { tabbedPane.removeAll(); if (tabbedPane.getTabCount() == 0) { cardLayout.show(this, "panel"); } } // --- PreferencesChangeListener --- // @Override public void preferencesChanged(Map preferences) { // Store preferences this.preferences = preferences; // Update layout if ("true".equals(preferences.get(TAB_LAYOUT))) { tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); } else { tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT); } setSelectedIndex(tabbedPane.getSelectedIndex()); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component.panel; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.api.model.TreeNodeData; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.component.Tree; import org.jd.gui.view.renderer.TreeNodeRenderer; import javax.swing.*; 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 java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Map; public class TreeTabbedPanel extends JPanel implements UriGettable, UriOpenable, PageChangeable, PageClosable, PreferencesChangeListener { protected API api; protected URI uri; protected Tree tree; protected TabbedPanel tabbedPanel; protected ArrayList pageChangedListeners = new ArrayList<>(); // Flags to prevent the event cascades protected boolean updateTreeMenuEnabled = true; protected boolean openUriEnabled = true; protected boolean treeNodeChangedEnabled = true; @SuppressWarnings("unchecked") public TreeTabbedPanel(API api, URI uri) { this.api = api; this.uri = uri; tree = new Tree(); tree.setShowsRootHandles(true); tree.setMinimumSize(new Dimension(150, 10)); tree.setExpandsSelectedPaths(true); tree.setCellRenderer(new TreeNodeRenderer() { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { // Always render the left tree with focus return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true); } }); tree.addTreeSelectionListener(e -> treeNodeChanged((T)tree.getLastSelectedPathComponent())); tree.addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeExpanded(TreeExpansionEvent e) { TreeNode node = (TreeNode)e.getPath().getLastPathComponent(); if (node instanceof TreeNodeExpandable) { TreeNodeExpandable tne = (TreeNodeExpandable)node; int oldHashCode = createHashCode(node.children()); tne.populateTreeNode(api); int newHashCode = createHashCode(node.children()); if (oldHashCode != newHashCode) { ((DefaultTreeModel)tree.getModel()).reload(node); } } } @Override public void treeCollapsed(TreeExpansionEvent e) {} }); tree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { TreePath path = tree.getPathForLocation(e.getX(), e.getY()); if (path != null) { tree.setSelectionPath(path); T node = (T)path.getLastPathComponent(); Collection actions = api.getContextualActions(node.getEntry(), node.getUri().getFragment()); if (actions != null) { JPopupMenu popup = new JPopupMenu(); for (Action action : actions) { if (action != null) { popup.add(action); } else { popup.addSeparator(); } } popup.show(e.getComponent(), e.getX(), e.getY()); } } } } }); tabbedPanel = new TabbedPanel(api); tabbedPanel.setMinimumSize(new Dimension(150, 10)); tabbedPanel.tabbedPane.addChangeListener(e -> pageChanged()); setLayout(new BorderLayout()); JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(tree), tabbedPanel); splitter.setResizeWeight(0.2); add(splitter, BorderLayout.CENTER); } protected static int createHashCode(Enumeration enumeration) { int hashCode = 1; while (enumeration.hasMoreElements()) { hashCode *= 31; Object element = enumeration.nextElement(); if (element != null) { hashCode += element.hashCode(); } } return hashCode; } @SuppressWarnings("unchecked") protected void treeNodeChanged(T node) { if (treeNodeChangedEnabled && (node != null)) { try { // Disable tabbedPane.changeListener updateTreeMenuEnabled = false; // Search base tree node URI uri = node.getUri(); if ((uri.getFragment() == null) && (uri.getQuery() == null)) { showPage(uri, uri, node); } else { URI baseUri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), null); T baseNode = node; while ((baseNode != null) && !baseNode.getUri().equals(baseUri)) { baseNode = (T)baseNode.getParent(); } if ((baseNode != null) && baseNode.getUri().equals(baseUri)) { showPage(uri, baseUri, baseNode); } } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } finally { // Enable tabbedPane.changeListener updateTreeMenuEnabled = true; } } } @SuppressWarnings("unchecked") protected

boolean showPage(URI uri, URI baseUri, DefaultMutableTreeNode baseNode) { P page = (P)tabbedPanel.showPage(baseUri); if ((page == null) && (baseNode instanceof PageCreator)) { page = ((PageCreator)baseNode).createPage(api); page.putClientProperty("node", baseNode); String path = baseUri.getPath(); String label = path.substring(path.lastIndexOf('/')+1); Object data = baseNode.getUserObject(); if (data instanceof TreeNodeData) { TreeNodeData tnd = (TreeNodeData)data; tabbedPanel.addPage(label, tnd.getIcon(), tnd.getTip(), page); } else { tabbedPanel.addPage(label, null, null, page); } } if (openUriEnabled && page instanceof UriOpenable) { ((UriOpenable)page).openUri(uri); } return (page != null); } @SuppressWarnings("unchecked") protected

void pageChanged() { try { // Disable highlight openUriEnabled = false; P page = (P)tabbedPanel.tabbedPane.getSelectedComponent(); if (updateTreeMenuEnabled) { // Synchronize tree if (page != null) { T node = (T)page.getClientProperty("node"); // Select tree node TreePath treePath = new TreePath(node.getPath()); tree.setSelectionPath(treePath); tree.scrollPathToVisible(treePath); } else { tree.clearSelection(); } } // Fire page changed event for (PageChangeListener listener : pageChangedListeners) { listener.pageChanged(page); } } finally { // Enable highlight openUriEnabled = true; } } // --- URIGetter --- // @Override public URI getUri() { return uri; } // --- URIOpener --- // @Override public boolean openUri(URI uri) { try { URI baseUri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), null); if (this.uri.equals(baseUri)) { return true; } else { DefaultMutableTreeNode node = searchTreeNode(baseUri, (DefaultMutableTreeNode) tree.getModel().getRoot()); if (showPage(uri, baseUri, node)) { DefaultMutableTreeNode childNode = searchTreeNode(uri, node); if (childNode != null) { node = childNode; } } if (node != null) { try { // Disable tree node changed listener treeNodeChangedEnabled = false; // Populate and expand node if (!(node instanceof PageCreator) && (node instanceof TreeNodeExpandable)) { ((TreeNodeExpandable) node).populateTreeNode(api); tree.expandPath(new TreePath(node.getPath())); } // Select tree node TreePath treePath = new TreePath(node.getPath()); tree.setSelectionPath(treePath); tree.scrollPathToVisible(treePath); } finally { // Enable tree node changed listener treeNodeChangedEnabled = true; } return true; } } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } return false; } @SuppressWarnings("unchecked") protected DefaultMutableTreeNode searchTreeNode(URI uri, DefaultMutableTreeNode node) { if (node instanceof TreeNodeExpandable) { ((TreeNodeExpandable)node).populateTreeNode(api); } String u = uri.toString(); T child = null; Enumeration enumeration = node.children(); while (enumeration.hasMoreElements()) { T element = (T)enumeration.nextElement(); String childU = element.getUri().toString(); if (u.length() > childU.length()) { if (u.startsWith(childU)) { char c = u.charAt(childU.length()); if ((c == '/') || (c == '!')) { child = element; break; } } } else if (u.equals(childU)) { child = element; break; } } if (child != null) { if (u.equals(child.getUri().toString())) { return child; } else { // Parent tree node found -> Recursive call return searchTreeNode(uri, child); } } else { // Not found return null; } } // --- PageChanger --- // @Override public void addPageChangeListener(PageChangeListener listener) { pageChangedListeners.add(listener); } // --- PageCloser --- // @Override public boolean closePage() { Component component = tabbedPanel.tabbedPane.getSelectedComponent(); if (component != null) { tabbedPanel.removeComponent(component); return true; } else { return false; } } // --- PreferencesChangeListener --- // @Override @SuppressWarnings("unchecked") public void preferencesChanged(Map preferences) { tabbedPanel.preferencesChanged(preferences); } } ================================================ FILE: app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.renderer; import org.jd.gui.view.bean.OpenTypeListCellBean; import javax.swing.*; import java.awt.*; public class OpenTypeListCellRenderer implements ListCellRenderer { protected Color textSelectionColor; protected Color textNonSelectionColor; protected Color infoSelectionColor; protected Color infoNonSelectionColor; protected Color backgroundSelectionColor; protected Color backgroundNonSelectionColor; protected JPanel panel; protected JLabel label, info; public OpenTypeListCellRenderer() { textSelectionColor = UIManager.getColor("List.selectionForeground"); textNonSelectionColor = UIManager.getColor("List.foreground"); backgroundSelectionColor = UIManager.getColor("List.selectionBackground"); backgroundNonSelectionColor = UIManager.getColor("List.background"); infoSelectionColor = infoColor(textSelectionColor); infoNonSelectionColor = infoColor(textNonSelectionColor); panel = new JPanel(new BorderLayout()); panel.add(label = new JLabel(), BorderLayout.WEST); panel.add(info = new JLabel(), BorderLayout.CENTER); } static protected Color infoColor(Color c) { if (c.getRed() + c.getGreen() + c.getBlue() > (3*127)) { return new Color( (int)((c.getRed()-127) *0.7 + 127), (int)((c.getGreen()-127)*0.7 + 127), (int)((c.getBlue()-127) *0.7 + 127), c.getAlpha()); } else { return new Color( (int)(127 - (127-c.getRed()) *0.7), (int)(127 - (127-c.getGreen())*0.7), (int)(127 - (127-c.getBlue()) *0.7), c.getAlpha()); } } @Override public Component getListCellRendererComponent(JList list, OpenTypeListCellBean value, int index, boolean selected, boolean hasFocus) { if (value != null) { // Display first level item label.setText(value.label); label.setIcon(value.icon); info.setText((value.packag != null) ? " - "+value.packag : ""); if (selected) { label.setForeground(textSelectionColor); info.setForeground(infoSelectionColor); panel.setBackground(backgroundSelectionColor); } else { label.setForeground(textNonSelectionColor); info.setForeground(infoNonSelectionColor); panel.setBackground(backgroundNonSelectionColor); } } else { label.setText(" ..."); label.setIcon(null); info.setText(""); label.setForeground(textNonSelectionColor); panel.setBackground(backgroundNonSelectionColor); } return panel; } } ================================================ FILE: app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.renderer; import org.jd.gui.api.model.TreeNodeData; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellRenderer; import java.awt.*; public class TreeNodeRenderer implements TreeCellRenderer { protected Color textSelectionColor; protected Color backgroundSelectionColor; protected Color textNonSelectionColor; protected Color backgroundNonSelectionColor; protected Color textDisabledColor; protected Color backgroundDisabledColor; protected JPanel panel; protected JLabel icon, label; public TreeNodeRenderer() { panel = new JPanel(new BorderLayout()); panel.add(icon = new JLabel(), BorderLayout.WEST); panel.add(label = new JLabel(), BorderLayout.CENTER); panel.setOpaque(false); textSelectionColor = UIManager.getColor("Tree.selectionForeground"); backgroundSelectionColor = UIManager.getColor("Tree.selectionBackground"); textNonSelectionColor = UIManager.getColor("Tree.textForeground"); backgroundNonSelectionColor = UIManager.getColor("Tree.textBackground"); textDisabledColor = UIManager.getColor("Tree.disabledText"); backgroundDisabledColor = UIManager.getColor("Tree.disabled"); Insets margins = UIManager.getInsets("Tree.rendererMargins"); icon.setForeground(textNonSelectionColor); icon.setOpaque(false); icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); label.setOpaque(false); if (margins != null) { label.setBorder(BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right)); } else { label.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4)); } } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { Object data = ((DefaultMutableTreeNode)value).getUserObject(); if (data instanceof TreeNodeData) { TreeNodeData tnd = (TreeNodeData)data; icon.setIcon(expanded && (tnd.getOpenIcon() != null) ? tnd.getOpenIcon() : tnd.getIcon()); label.setText(tnd.getLabel()); } else { icon.setIcon(null); label.setText("" + data); } if (selected) { if (hasFocus) { label.setForeground(textSelectionColor); label.setBackground(backgroundSelectionColor); } else { label.setForeground(textDisabledColor); label.setBackground(backgroundDisabledColor); } label.setOpaque(true); } else { label.setForeground(textNonSelectionColor); label.setOpaque(false); } return panel; } } ================================================ FILE: app/src/main/resources/META-INF/services/org.jd.gui.spi.PanelFactory ================================================ org.jd.gui.service.mainpanel.ContainerPanelFactoryProvider ================================================ FILE: app/src/main/resources/META-INF/services/org.jd.gui.spi.PreferencesPanel ================================================ org.jd.gui.service.preferencespanel.UISingleInstancePreferencesProvider org.jd.gui.service.preferencespanel.UITabsPreferencesProvider ================================================ FILE: build.gradle ================================================ buildscript { repositories { jcenter() } dependencies { classpath 'com.netflix.nebula:gradle-ospackage-plugin:5.3.0' // RPM & DEB support classpath 'edu.sc.seis.gradle:launch4j:2.4.4' classpath 'net.sf.proguard:proguard-gradle:6.1.0' } } apply plugin: 'java' apply plugin: 'distribution' apply plugin: 'edu.sc.seis.launch4j' apply plugin: 'nebula.ospackage' // Common configuration // rootProject.version='1.6.6' rootProject.ext.set('jdCoreVersion', '1.1.3') targetCompatibility = '1.8' allprojects { apply plugin: 'eclipse' apply plugin: 'idea' tasks.withType(JavaCompile) { sourceCompatibility = targetCompatibility = '1.8' options.compilerArgs << '-Xlint:deprecation' options.compilerArgs << '-Xlint:unchecked' options.encoding = 'UTF-8' } repositories { jcenter() } configurations { provided compile.extendsFrom provided } } // 'cleanIdea' task extension // cleanIdea.doFirst { delete project.name + '.iws' delete 'out' followSymlinks = true } // All in one JAR file // subprojects.each { subproject -> evaluationDependsOn(subproject.path) } jar { dependsOn subprojects.tasks['jar'] // Add SPI directory def tmpSpiDir = file('build/tmp/spi') from tmpSpiDir // Add dependencies def deps = [] subprojects.each { subproject -> from subproject.sourceSets.main.output.classesDirs from subproject.sourceSets.main.output.resourcesDir deps += subproject.configurations.runtime - subproject.configurations.provided } subprojects.each { subproject -> deps -= subproject.jar.archivePath } deps = deps.unique().collect { it.isDirectory() ? it : zipTree(it) } from deps manifest { attributes 'Main-Class': 'org.jd.gui.App', 'SplashScreen-Image': 'org/jd/gui/images/jd_icon_128.png', 'JD-GUI-Version': project.version, 'JD-Core-Version': project.jdCoreVersion } exclude 'META-INF/licenses/**', 'META-INF/maven/**', 'META-INF/INDEX.LIST' exclude '**/ErrorStrip_*.properties', '**/RSyntaxTextArea_*.properties', '**/RTextArea_*.properties' exclude '**/FocusableTip_*.properties', '**/RSyntaxTextArea_License.txt' duplicatesStrategy DuplicatesStrategy.EXCLUDE doFirst { // Create SPI directory tmpSpiDir.deleteDir() def tmpSpiServicesDir = file(tmpSpiDir.path + '/META-INF/services') tmpSpiServicesDir.mkdirs() // Copy and merge SPI config files subprojects.each { subproject -> def servicesDir = file(subproject.sourceSets.main.output.resourcesDir.path + '/META-INF/services') if (servicesDir.exists()) { servicesDir.eachFile { serviceFile -> def target = file(tmpSpiServicesDir.path + '/' + serviceFile.name) target << serviceFile.text } } } } } // Minify JAR file // task proguard(type: proguard.gradle.ProGuardTask, dependsOn: 'jar') { configuration 'src/proguard/resources/proguard.config.txt' injars jar.archivePath outjars 'build/libs/' + project.name + '-' + project.version + '-min.jar' libraryjars System.getProperty('java.home') + '/lib/rt.jar' libraryjars System.getProperty('java.home') + '/jmods/' } // Java executable wrapper for Windows // launch4j { createExe.dependsOn 'proguard' version = textVersion = project.version fileDescription = productName = 'JD-GUI' errTitle 'JD-GUI Windows Wrapper' copyright 'JD-GUI (C) 2008-2019 Emmanuel Dupuy' icon projectDir.path + '/src/launch4j/resources/images/jd-gui.ico' jar projectDir.path + '/' + proguard.outJarFiles[0] bundledJrePath = '%JAVA_HOME%' } // Packages for Linux // ospackage { buildDeb.dependsOn 'proguard' buildRpm.dependsOn 'proguard' license = file('LICENSE') maintainer 'Emmanuel Dupuy ' os LINUX packageDescription 'JD-GUI, a standalone graphical utility that displays Java sources from CLASS files' packageGroup 'java' packageName project.name release '0' summary 'A Java Decompiler' url 'https://github.com/java-decompiler/jd-gui' into '/opt/' + project.name from (proguard.outJarFiles[0]) { fileMode 0755 } from ('src/linux/resources/') { fileMode 0755 } from 'LICENSE', 'NOTICE', 'README.md' postInstall 'cd /opt/' + project.name + '; ln -s ./' + file(proguard.outJarFiles[0]).name + ' ./jd-gui.jar; xdg-icon-resource install --size 128 --novendor ./jd_icon_128.png jd-gui; xdg-desktop-menu install ./*.desktop' preUninstall 'cd /opt/' + project.name + '; rm -f ./jd-gui.jar; rm -fr ./ext; xdg-desktop-menu uninstall ./*.desktop' } // Distributions for OSX and Windows // distributions { osx.contents { into('JD-GUI.app/Contents') { from('src/osx/resources') { include 'Info.plist' expand VERSION: project.version, JAR: file(proguard.outJarFiles[0]).name } } into('JD-GUI.app/Contents/MacOS') { from('src/osx/resources') { include 'universalJavaApplicationStub.sh' fileMode 0755 } } into('JD-GUI.app/Contents/Resources/Java') { from proguard.outJarFiles[0] } from 'LICENSE', 'NOTICE', 'README.md' } windows.contents { from 'build/launch4j/jd-gui.exe' from 'LICENSE', 'NOTICE', 'README.md' } installWindowsDist.dependsOn createExe windowsDistTar.dependsOn createExe windowsDistZip.dependsOn createExe installOsxDist.dependsOn 'proguard' osxDistTar.dependsOn 'proguard' osxDistZip.dependsOn 'proguard' } build.finalizedBy buildDeb build.finalizedBy buildRpm ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Sat Mar 02 11:11:32 CET 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: services/build.gradle ================================================ apply plugin: 'java' dependencies { compile 'com.fifesoft:rsyntaxtextarea:3.0.4' compile 'org.ow2.asm:asm:7.1' compile 'org.jd:jd-core:' + parent.jdCoreVersion compile project(':api') testCompile 'junit:junit:4.12' } version = parent.version // ANTLR // ext.antlr4 = [ antlrSource: 'src/main/antlr', destinationDir: 'src-generated/antlr/java', grammarPackage: 'org.jd.gui.util.parser.antlr' ] configurations { antlr4 { description = "ANTLR4" } } dependencies { compile 'org.antlr:antlr4-runtime:4.5' antlr4 'org.antlr:antlr4:4.5' } task antlr4OutputDir() { mkdir antlr4.destinationDir } task antlr4GenerateGrammarSource(dependsOn: antlr4OutputDir, type: JavaExec) { description = 'Generates Java sources from ANTLR4 grammars.' inputs.dir file(antlr4.antlrSource) outputs.dir file(antlr4.destinationDir) def grammars = fileTree(antlr4.antlrSource).include('**/*.g4') def pkg = antlr4.grammarPackage.replaceAll("\\.", "/") main = 'org.antlr.v4.Tool' classpath = configurations.antlr4 args = ['-o', "${antlr4.destinationDir}/${pkg}", '-package', antlr4.grammarPackage, grammars.files].flatten() } compileJava { dependsOn antlr4GenerateGrammarSource source antlr4.destinationDir } clean { delete 'src-generated' } idea.module { sourceDirs += file(antlr4.destinationDir) } ideaModule.dependsOn antlr4GenerateGrammarSource eclipse.classpath.file.withXml { xml -> def node = xml.asNode() node.appendNode( 'classpathentry', [ kind: 'src', path: antlr4.destinationDir]) } eclipseClasspath.dependsOn antlr4GenerateGrammarSource ================================================ FILE: services/src/main/antlr/Java.g4 ================================================ /* [The "BSD licence"] Copyright (c) 2013 Terence Parr, Sam Harwell All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. */ /** A Java 1.7 grammar for ANTLR v4 derived from ANTLR v3 Java grammar. * Uses ANTLR v4's left-recursive expression notation. * It parses ECJ, Netbeans, JDK etc... * * Sam Harwell cleaned this up significantly and updated to 1.7! * * You can test with * * $ antlr4 Java.g4 * $ javac *.java * $ grun Java compilationUnit *.java */ grammar Java; // starting point for parsing a java file compilationUnit : packageDeclaration? importDeclaration* typeDeclaration* EOF ; packageDeclaration : annotation* 'package' qualifiedName ';' ; importDeclaration : 'import' 'static'? qualifiedName ('.' '*')? ';' ; typeDeclaration : classOrInterfaceModifier* classDeclaration | classOrInterfaceModifier* enumDeclaration | classOrInterfaceModifier* interfaceDeclaration | classOrInterfaceModifier* annotationTypeDeclaration | ';' ; modifier : classOrInterfaceModifier | ( 'native' | 'synchronized' | 'transient' | 'volatile' ) ; classOrInterfaceModifier : annotation // class or interface | ( 'public' // class or interface | 'protected' // class or interface | 'private' // class or interface | 'static' // class or interface | 'abstract' // class or interface | 'final' // class only -- does not apply to interfaces | 'strictfp' // class or interface ) ; variableModifier : 'final' | annotation ; classDeclaration : 'class' Identifier typeParameters? ('extends' type)? ('implements' typeList)? classBody ; typeParameters : '<' typeParameter (',' typeParameter)* '>' ; typeParameter : Identifier ('extends' typeBound)? ; typeBound : type ('&' type)* ; enumDeclaration : ENUM Identifier ('implements' typeList)? '{' enumConstants? ','? enumBodyDeclarations? '}' ; enumConstants : enumConstant (',' enumConstant)* ; enumConstant : annotation* Identifier arguments? classBody? ; enumBodyDeclarations : ';' classBodyDeclaration* ; interfaceDeclaration : 'interface' Identifier typeParameters? ('extends' typeList)? interfaceBody ; typeList : type (',' type)* ; classBody : '{' classBodyDeclaration* '}' ; interfaceBody : '{' interfaceBodyDeclaration* '}' ; classBodyDeclaration : ';' | 'static'? block | modifier* memberDeclaration ; memberDeclaration : methodDeclaration | genericMethodDeclaration | fieldDeclaration | constructorDeclaration | genericConstructorDeclaration | interfaceDeclaration | annotationTypeDeclaration | classDeclaration | enumDeclaration ; /* We use rule this even for void methods which cannot have [] after parameters. This simplifies grammar and we can consider void to be a type, which renders the [] matching as a context-sensitive issue or a semantic check for invalid return type after parsing. */ methodDeclaration : (type|'void') Identifier formalParameters ('[' ']')* ('throws' qualifiedNameList)? ( methodBody | ';' ) ; genericMethodDeclaration : typeParameters methodDeclaration ; constructorDeclaration : Identifier formalParameters ('throws' qualifiedNameList)? constructorBody ; genericConstructorDeclaration : typeParameters constructorDeclaration ; fieldDeclaration : type variableDeclarators ';' ; interfaceBodyDeclaration : modifier* interfaceMemberDeclaration | ';' ; interfaceMemberDeclaration : constDeclaration | interfaceMethodDeclaration | genericInterfaceMethodDeclaration | interfaceDeclaration | annotationTypeDeclaration | classDeclaration | enumDeclaration ; constDeclaration : type constantDeclarator (',' constantDeclarator)* ';' ; constantDeclarator : Identifier ('[' ']')* '=' variableInitializer ; // see matching of [] comment in methodDeclaratorRest interfaceMethodDeclaration : (type|'void') Identifier formalParameters ('[' ']')* ('throws' qualifiedNameList)? ';' ; genericInterfaceMethodDeclaration : typeParameters interfaceMethodDeclaration ; variableDeclarators : variableDeclarator (',' variableDeclarator)* ; variableDeclarator : variableDeclaratorId ('=' variableInitializer)? ; variableDeclaratorId : Identifier ('[' ']')* ; variableInitializer : arrayInitializer | expression ; arrayInitializer : '{' (variableInitializer (',' variableInitializer)* (',')? )? '}' ; enumConstantName : Identifier ; type : classOrInterfaceType ('[' ']')* | primitiveType ('[' ']')* ; classOrInterfaceType : Identifier typeArguments? ('.' Identifier typeArguments? )* ; primitiveType : 'boolean' | 'char' | 'byte' | 'short' | 'int' | 'long' | 'float' | 'double' ; typeArguments : '<' typeArgument (',' typeArgument)* '>' ; typeArgument : type | '?' (('extends' | 'super') type)? ; qualifiedNameList : qualifiedName (',' qualifiedName)* ; formalParameters : '(' formalParameterList? ')' ; formalParameterList : formalParameter (',' formalParameter)* (',' lastFormalParameter)? | lastFormalParameter ; formalParameter : variableModifier* type variableDeclaratorId ; lastFormalParameter : variableModifier* type '...' variableDeclaratorId ; methodBody : block ; constructorBody : block ; qualifiedName : Identifier ('.' Identifier)* ; literal : IntegerLiteral | FloatingPointLiteral | CharacterLiteral | StringLiteral | BooleanLiteral | 'null' ; // ANNOTATIONS annotation : '@' annotationName ( '(' ( elementValuePairs | elementValue )? ')' )? ; annotationName : qualifiedName ; elementValuePairs : elementValuePair (',' elementValuePair)* ; elementValuePair : Identifier '=' elementValue ; elementValue : expression | annotation | elementValueArrayInitializer ; elementValueArrayInitializer : '{' (elementValue (',' elementValue)*)? (',')? '}' ; annotationTypeDeclaration : '@' 'interface' Identifier annotationTypeBody ; annotationTypeBody : '{' (annotationTypeElementDeclaration)* '}' ; annotationTypeElementDeclaration : modifier* annotationTypeElementRest | ';' // this is not allowed by the grammar, but apparently allowed by the actual compiler ; annotationTypeElementRest : type annotationMethodOrConstantRest ';' | classDeclaration ';'? | interfaceDeclaration ';'? | enumDeclaration ';'? | annotationTypeDeclaration ';'? ; annotationMethodOrConstantRest : annotationMethodRest | annotationConstantRest ; annotationMethodRest : Identifier '(' ')' defaultValue? ; annotationConstantRest : variableDeclarators ; defaultValue : 'default' elementValue ; // STATEMENTS / BLOCKS block : '{' blockStatement* '}' ; blockStatement : localVariableDeclarationStatement | statement | typeDeclaration ; localVariableDeclarationStatement : localVariableDeclaration ';' ; localVariableDeclaration : variableModifier* type variableDeclarators ; statement : block | ASSERT expression (':' expression)? ';' | 'if' parExpression statement ('else' statement)? | 'for' '(' forControl ')' statement | 'while' parExpression statement | 'do' statement 'while' parExpression ';' | 'try' block (catchClause+ finallyBlock? | finallyBlock) | 'try' resourceSpecification block catchClause* finallyBlock? | 'switch' parExpression '{' switchBlockStatementGroup* switchLabel* '}' | 'synchronized' parExpression block | 'return' expression? ';' | 'throw' expression ';' | 'break' Identifier? ';' | 'continue' Identifier? ';' | ';' | statementExpression ';' | Identifier ':' statement ; catchClause : 'catch' '(' variableModifier* catchType Identifier ')' block ; catchType : qualifiedName ('|' qualifiedName)* ; finallyBlock : 'finally' block ; resourceSpecification : '(' resources ';'? ')' ; resources : resource (';' resource)* ; resource : variableModifier* classOrInterfaceType variableDeclaratorId '=' expression ; /** Matches cases then statements, both of which are mandatory. * To handle empty cases at the end, we add switchLabel* to statement. */ switchBlockStatementGroup : switchLabel+ blockStatement+ ; switchLabel : 'case' constantExpression ':' | 'case' enumConstantName ':' | 'default' ':' ; forControl : enhancedForControl | forInit? ';' expression? ';' forUpdate? ; forInit : localVariableDeclaration | expressionList ; enhancedForControl : variableModifier* type variableDeclaratorId ':' expression ; forUpdate : expressionList ; // EXPRESSIONS parExpression : '(' expression ')' ; expressionList : expression (',' expression)* ; statementExpression : expression ; constantExpression : expression ; expression : primary | expression '.' Identifier | expression '.' 'this' | expression '.' 'new' nonWildcardTypeArguments? innerCreator | expression '.' 'super' superSuffix | expression '.' explicitGenericInvocation | expression '[' expression ']' | expression '(' expressionList? ')' | 'new' creator | '(' type ')' expression | expression ('++' | '--') | ('+'|'-'|'++'|'--') expression | ('~'|'!') expression | expression ('*'|'/'|'%') expression | expression ('+'|'-') expression | expression ('<' '<' | '>' '>' '>' | '>' '>') expression | expression ('<=' | '>=' | '>' | '<') expression | expression 'instanceof' type | expression ('==' | '!=') expression | expression '&' expression | expression '^' expression | expression '|' expression | expression '&&' expression | expression '||' expression | expression '?' expression ':' expression | expression ( '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' | '>>=' | '>>>=' | '<<=' | '%=' ) expression ; primary : '(' expression ')' | 'this' | 'super' | literal | Identifier | type '.' 'class' | 'void' '.' 'class' | nonWildcardTypeArguments (explicitGenericInvocationSuffix | 'this' arguments) ; creator : nonWildcardTypeArguments createdName classCreatorRest | createdName (arrayCreatorRest | classCreatorRest) ; createdName : Identifier typeArgumentsOrDiamond? ('.' Identifier typeArgumentsOrDiamond?)* | primitiveType ; innerCreator : Identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest ; arrayCreatorRest : '[' ( ']' ('[' ']')* arrayInitializer | expression ']' ('[' expression ']')* ('[' ']')* ) ; classCreatorRest : arguments classBody? ; explicitGenericInvocation : nonWildcardTypeArguments explicitGenericInvocationSuffix ; nonWildcardTypeArguments : '<' typeList '>' ; typeArgumentsOrDiamond : '<' '>' | typeArguments ; nonWildcardTypeArgumentsOrDiamond : '<' '>' | nonWildcardTypeArguments ; superSuffix : arguments | '.' Identifier arguments? ; explicitGenericInvocationSuffix : 'super' superSuffix | Identifier arguments ; arguments : '(' expressionList? ')' ; // LEXER // §3.9 Keywords ABSTRACT : 'abstract'; ASSERT : 'assert'; BOOLEAN : 'boolean'; BREAK : 'break'; BYTE : 'byte'; CASE : 'case'; CATCH : 'catch'; CHAR : 'char'; CLASS : 'class'; CONST : 'const'; CONTINUE : 'continue'; DEFAULT : 'default'; DO : 'do'; DOUBLE : 'double'; ELSE : 'else'; ENUM : 'enum'; EXTENDS : 'extends'; FINAL : 'final'; FINALLY : 'finally'; FLOAT : 'float'; FOR : 'for'; IF : 'if'; GOTO : 'goto'; IMPLEMENTS : 'implements'; IMPORT : 'import'; INSTANCEOF : 'instanceof'; INT : 'int'; INTERFACE : 'interface'; LONG : 'long'; NATIVE : 'native'; NEW : 'new'; PACKAGE : 'package'; PRIVATE : 'private'; PROTECTED : 'protected'; PUBLIC : 'public'; RETURN : 'return'; SHORT : 'short'; STATIC : 'static'; STRICTFP : 'strictfp'; SUPER : 'super'; SWITCH : 'switch'; SYNCHRONIZED : 'synchronized'; THIS : 'this'; THROW : 'throw'; THROWS : 'throws'; TRANSIENT : 'transient'; TRY : 'try'; VOID : 'void'; VOLATILE : 'volatile'; WHILE : 'while'; // §3.10.1 Integer Literals IntegerLiteral : DecimalIntegerLiteral | HexIntegerLiteral | OctalIntegerLiteral | BinaryIntegerLiteral ; fragment DecimalIntegerLiteral : DecimalNumeral IntegerTypeSuffix? ; fragment HexIntegerLiteral : HexNumeral IntegerTypeSuffix? ; fragment OctalIntegerLiteral : OctalNumeral IntegerTypeSuffix? ; fragment BinaryIntegerLiteral : BinaryNumeral IntegerTypeSuffix? ; fragment IntegerTypeSuffix : [lL] ; fragment DecimalNumeral : '0' | NonZeroDigit (Digits? | Underscores Digits) ; fragment Digits : Digit (DigitOrUnderscore* Digit)? ; fragment Digit : '0' | NonZeroDigit ; fragment NonZeroDigit : [1-9] ; fragment DigitOrUnderscore : Digit | '_' ; fragment Underscores : '_'+ ; fragment HexNumeral : '0' [xX] HexDigits ; fragment HexDigits : HexDigit (HexDigitOrUnderscore* HexDigit)? ; fragment HexDigit : [0-9a-fA-F] ; fragment HexDigitOrUnderscore : HexDigit | '_' ; fragment OctalNumeral : '0' Underscores? OctalDigits ; fragment OctalDigits : OctalDigit (OctalDigitOrUnderscore* OctalDigit)? ; fragment OctalDigit : [0-7] ; fragment OctalDigitOrUnderscore : OctalDigit | '_' ; fragment BinaryNumeral : '0' [bB] BinaryDigits ; fragment BinaryDigits : BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)? ; fragment BinaryDigit : [01] ; fragment BinaryDigitOrUnderscore : BinaryDigit | '_' ; // §3.10.2 Floating-Point Literals FloatingPointLiteral : DecimalFloatingPointLiteral | HexadecimalFloatingPointLiteral ; fragment DecimalFloatingPointLiteral : Digits '.' Digits? ExponentPart? FloatTypeSuffix? | '.' Digits ExponentPart? FloatTypeSuffix? | Digits ExponentPart FloatTypeSuffix? | Digits FloatTypeSuffix ; fragment ExponentPart : ExponentIndicator SignedInteger ; fragment ExponentIndicator : [eE] ; fragment SignedInteger : Sign? Digits ; fragment Sign : [+-] ; fragment FloatTypeSuffix : [fFdD] ; fragment HexadecimalFloatingPointLiteral : HexSignificand BinaryExponent FloatTypeSuffix? ; fragment HexSignificand : HexNumeral '.'? | '0' [xX] HexDigits? '.' HexDigits ; fragment BinaryExponent : BinaryExponentIndicator SignedInteger ; fragment BinaryExponentIndicator : [pP] ; // §3.10.3 Boolean Literals BooleanLiteral : 'true' | 'false' ; // §3.10.4 Character Literals CharacterLiteral : '\'' SingleCharacter '\'' | '\'' EscapeSequence '\'' ; fragment SingleCharacter : ~['\\] ; // §3.10.5 String Literals StringLiteral : '"' StringCharacters? '"' ; fragment StringCharacters : StringCharacter+ ; fragment StringCharacter : ~["\\] | EscapeSequence ; // §3.10.6 Escape Sequences for Character and String Literals fragment EscapeSequence : '\\' [btnfr"'\\] | OctalEscape | UnicodeEscape ; fragment OctalEscape : '\\' OctalDigit | '\\' OctalDigit OctalDigit | '\\' ZeroToThree OctalDigit OctalDigit ; fragment UnicodeEscape : '\\' 'u' HexDigit HexDigit HexDigit HexDigit ; fragment ZeroToThree : [0-3] ; // §3.10.7 The Null Literal NullLiteral : 'null' ; // §3.11 Separators LPAREN : '('; RPAREN : ')'; LBRACE : '{'; RBRACE : '}'; LBRACK : '['; RBRACK : ']'; SEMI : ';'; COMMA : ','; DOT : '.'; // §3.12 Operators ASSIGN : '='; GT : '>'; LT : '<'; BANG : '!'; TILDE : '~'; QUESTION : '?'; COLON : ':'; EQUAL : '=='; LE : '<='; GE : '>='; NOTEQUAL : '!='; AND : '&&'; OR : '||'; INC : '++'; DEC : '--'; ADD : '+'; SUB : '-'; MUL : '*'; DIV : '/'; BITAND : '&'; BITOR : '|'; CARET : '^'; MOD : '%'; ADD_ASSIGN : '+='; SUB_ASSIGN : '-='; MUL_ASSIGN : '*='; DIV_ASSIGN : '/='; AND_ASSIGN : '&='; OR_ASSIGN : '|='; XOR_ASSIGN : '^='; MOD_ASSIGN : '%='; LSHIFT_ASSIGN : '<<='; RSHIFT_ASSIGN : '>>='; URSHIFT_ASSIGN : '>>>='; // §3.8 Identifiers (must appear after all keywords in the grammar) Identifier : JavaLetter JavaLetterOrDigit* ; fragment JavaLetter : [a-zA-Z$_] // these are the "java letters" below 0xFF | // covers all characters above 0xFF which are not a surrogate ~[\u0000-\u00FF\uD800-\uDBFF] {Character.isJavaIdentifierStart(_input.LA(-1))}? | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF [\uD800-\uDBFF] [\uDC00-\uDFFF] {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? ; fragment JavaLetterOrDigit : [a-zA-Z0-9$_] // these are the "java letters or digits" below 0xFF | // covers all characters above 0xFF which are not a surrogate ~[\u0000-\u00FF\uD800-\uDBFF] {Character.isJavaIdentifierPart(_input.LA(-1))}? | // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF [\uD800-\uDBFF] [\uDC00-\uDFFF] {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? ; // // Additional symbols not defined in the lexical specification // AT : '@'; ELLIPSIS : '...'; // // Whitespace and comments // WS : [ \t\r\n\u000C]+ -> skip ; COMMENT : '/*' .*? '*/' -> skip ; LINE_COMMENT : '//' ~[\r\n]* -> skip ; ================================================ FILE: services/src/main/java/org/fife/ui/rtextarea/Marker.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.fife.ui.rtextarea; import org.fife.ui.rsyntaxtextarea.DocumentRange; import java.util.List; /* * An utility class to call the restricted access methods of 'RTextArea'. * * JD-GUI uses two workarounds for RSyntaxTextArea: * - org.fife.ui.rtextarea.Marker * - org.jd.gui.view.component.RoundMarkErrorStrip */ public class Marker { public static void markAll(RTextArea textArea, List ranges) { textArea.markAll(ranges); } public static void clearMarkAllHighlights(RTextArea textArea) { textArea.clearMarkAllHighlights(); } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.model.Container; import java.util.Comparator; /** * Directories before files, sorted by path */ public class ContainerEntryComparator implements Comparator { public static final ContainerEntryComparator COMPARATOR = new ContainerEntryComparator(); public int compare(Container.Entry e1, Container.Entry e2) { if (e1.isDirectory()) { if (!e2.isDirectory()) { return -1; } } else { if (e2.isDirectory()) { return 1; } } return e1.getPath().compareTo(e2.getPath()); } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/EarContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public class EarContainer extends GenericContainer { public EarContainer(API api, Container.Entry parentEntry, Path rootPath) { super(api, parentEntry, rootPath); } public String getType() { return "ear"; } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/GenericContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; public class GenericContainer implements Container { protected static final long TIMESTAMP = System.currentTimeMillis(); protected static long tmpFileCounter = 0; protected API api; protected int rootNameCount; protected Container.Entry root; public GenericContainer(API api, Container.Entry parentEntry, Path rootPath) { try { URI uri = parentEntry.getUri(); this.api = api; this.rootNameCount = rootPath.getNameCount(); this.root = new Entry(parentEntry, rootPath, new URI(uri.getScheme(), uri.getHost(), uri.getPath() + "!/", null)) { public Entry newChildEntry(Path fsPath) { return new Entry(parent, fsPath, null); } }; } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } public String getType() { return "generic"; } public Container.Entry getRoot() { return root; } protected class Entry implements Container.Entry { protected Container.Entry parent; protected Path fsPath; protected String strPath; protected URI uri; protected Boolean isDirectory; protected Collection children; public Entry(Container.Entry parent, Path fsPath, URI uri) { this.parent = parent; this.fsPath = fsPath; this.strPath = null; this.uri = uri; this.isDirectory = null; this.children = null; } public Entry newChildEntry(Path fsPath) { return new Entry(this, fsPath, null); } public Container getContainer() { return GenericContainer.this; } public Container.Entry getParent() { return parent; } public URI getUri() { if (uri == null) { try { URI rootUri = root.getUri(); uri = new URI(rootUri.getScheme(), rootUri.getHost(), rootUri.getPath() + getPath(), null); } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } return uri; } public String getPath() { if (strPath == null) { int nameCount = fsPath.getNameCount(); if (rootNameCount == nameCount) { strPath = ""; } else { strPath = fsPath.subpath(rootNameCount, nameCount).toString().replace(fsPath.getFileSystem().getSeparator(), "/"); int strPathLength = strPath.length(); if ((strPathLength > 0) && strPath.charAt(strPathLength-1) == '/') { // Cut last separator strPath = strPath.substring(0, strPathLength-1); } } } return strPath; } public boolean isDirectory() { if (isDirectory == null) { isDirectory = Boolean.valueOf(Files.isDirectory(fsPath)); } return isDirectory; } public long length() { try { return Files.size(fsPath); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); return -1L; } } public InputStream getInputStream() { try { return Files.newInputStream(fsPath); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); return null; } } public Collection getChildren() { if (children == null) { try { if (Files.isDirectory(fsPath)) { children = loadChildrenFromDirectoryEntry(); } else { children = loadChildrenFromFileEntry(); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } return children; } protected Collection loadChildrenFromDirectoryEntry() throws IOException { try (DirectoryStream stream = Files.newDirectoryStream(fsPath)) { ArrayList children = new ArrayList<>(); int parentNameCount = fsPath.getNameCount(); for (Path subPath : stream) { if (subPath.getNameCount() > parentNameCount) { children.add(newChildEntry(subPath)); } } children.sort(ContainerEntryComparator.COMPARATOR); return Collections.unmodifiableCollection(children); } } protected Collection loadChildrenFromFileEntry() throws IOException { StringBuilder suffix = new StringBuilder(".").append(TIMESTAMP).append('.').append(tmpFileCounter++).append('.').append(fsPath.getFileName().toString()); File tmpFile = File.createTempFile("jd-gui.tmp.", suffix.toString()); Path tmpPath = Paths.get(tmpFile.toURI()); tmpFile.delete(); tmpFile.deleteOnExit(); Files.copy(fsPath, tmpPath); FileSystem subFileSystem = FileSystems.newFileSystem(tmpPath, null); if (subFileSystem != null) { Iterator rootDirectories = subFileSystem.getRootDirectories().iterator(); if (rootDirectories.hasNext()) { Path rootPath = rootDirectories.next(); ContainerFactory containerFactory = api.getContainerFactory(rootPath); if (containerFactory != null) { Container container = containerFactory.make(api, this, rootPath); if (container != null) { return container.getRoot().getChildren(); } } } } tmpFile.delete(); return Collections.emptyList(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/JarContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public class JarContainer extends GenericContainer { public JarContainer(API api, Container.Entry parentEntry, Path rootPath) { super(api, parentEntry, rootPath); } public String getType() { return "jar"; } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/JavaModuleContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public class JavaModuleContainer extends GenericContainer { public JavaModuleContainer(API api, Container.Entry parentEntry, Path rootPath) { super(api, parentEntry, rootPath); } public String getType() { return "jmod"; } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/KarContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public class KarContainer extends GenericContainer { public KarContainer(API api, Container.Entry parentEntry, Path rootPath) { super(api, parentEntry, rootPath); } public String getType() { return "kar"; } } ================================================ FILE: services/src/main/java/org/jd/gui/model/container/WarContainer.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.model.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import java.nio.file.Path; public class WarContainer extends GenericContainer { public WarContainer(API api, Container.Entry parentEntry, Path rootPath) { super(api, parentEntry, rootPath); } public String getType() { return "war"; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.actions; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.spi.ContextualActionsFactory; import org.jd.gui.spi.TypeFactory; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.util.Collection; import java.util.Collections; public class CopyQualifiedNameContextualActionsFactory implements ContextualActionsFactory { public Collection make(API api, Container.Entry entry, String fragment) { return Collections.singletonList(new CopyQualifiedNameAction(api, entry, fragment)); } public static class CopyQualifiedNameAction extends AbstractAction { protected static final ImageIcon ICON = new ImageIcon(CopyQualifiedNameAction.class.getClassLoader().getResource("org/jd/gui/images/cpyqual_menu.png")); protected API api; protected Container.Entry entry; protected String fragment; public CopyQualifiedNameAction(API api, Container.Entry entry, String fragment) { this.api = api; this.entry = entry; this.fragment = fragment; putValue(GROUP_NAME, "Edit > CutCopyPaste"); putValue(NAME, "Copy Qualified Name"); putValue(SMALL_ICON, ICON); } public void actionPerformed(ActionEvent e) { TypeFactory typeFactory = api.getTypeFactory(entry); if (typeFactory != null) { Type type = typeFactory.make(api, entry, fragment); if (type != null) { StringBuilder sb = new StringBuilder(type.getDisplayPackageName()); if (sb.length() > 0) { sb.append('.'); } sb.append(type.getDisplayTypeName()); if (fragment != null) { int dashIndex = fragment.indexOf('-'); if (dashIndex != -1) { int lastDashIndex = fragment.lastIndexOf('-'); if (dashIndex == lastDashIndex) { // See jd.gui.api.feature.UriOpenable throw new InvalidFormatException("fragment: " + fragment); } else { String name = fragment.substring(dashIndex + 1, lastDashIndex); String descriptor = fragment.substring(lastDashIndex + 1); if (descriptor.startsWith("(")) { for (Type.Method method : type.getMethods()) { if (method.getName().equals(name) && method.getDescriptor().equals(descriptor)) { sb.append('.').append(method.getDisplayName()); break; } } } else { for (Type.Field field : type.getFields()) { if (field.getName().equals(name) && field.getDescriptor().equals(descriptor)) { sb.append('.').append(field.getDisplayName()); break; } } } } } } Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(sb.toString()), null); return; } } // Create qualified name from URI String path = entry.getUri().getPath(); String rootPath = entry.getContainer().getRoot().getUri().getPath(); String qualifiedName = path.substring(rootPath.length()).replace('/', '.'); if (qualifiedName.endsWith(".class")) { qualifiedName = qualifiedName.substring(0, qualifiedName.length()-6); } Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(qualifiedName), null); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.actions; public class InvalidFormatException extends RuntimeException{ public InvalidFormatException(String message) { super(message); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.EarContainer; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; public class EarContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "ear"; } @Override public boolean accept(API api, Path rootPath) { if (rootPath.toUri().toString().toLowerCase().endsWith(".ear!/")) { return true; } else { // Extension: accept uncompressed EAR file containing a folder 'META-INF/application.xml' try { return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("META-INF/application.xml")); } catch (InvalidPathException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new EarContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.GenericContainer; import org.jd.gui.spi.ContainerFactory; import java.nio.file.Path; public class GenericContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "generic"; } @Override public boolean accept(API api, Path rootPath) { return true; } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new GenericContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.JarContainer; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; public class JarContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "jar"; } @Override public boolean accept(API api, Path rootPath) { if (rootPath.toUri().toString().toLowerCase().endsWith(".jar!/")) { // Specification: http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html return true; } else { // Extension: accept uncompressed JAR file containing a folder 'META-INF' try { return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("META-INF")); } catch (InvalidPathException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new JarContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/JavaModuleContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.JavaModuleContainer; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; public class JavaModuleContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "jmod"; } @Override public boolean accept(API api, Path rootPath) { if (rootPath.toUri().toString().toLowerCase().endsWith(".jmod!/")) { return true; } else { // Extension: accept uncompressed JMOD file containing a folder 'classes' try { return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("classes")); } catch (InvalidPathException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new JavaModuleContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/KarContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.KarContainer; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; public class KarContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "kar"; } @Override public boolean accept(API api, Path rootPath) { if (rootPath.toUri().toString().toLowerCase().endsWith(".kar!/")) { return true; } else { // Extension: accept uncompressed KAR file containing a folder 'repository' try { return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("repository")); } catch (InvalidPathException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new KarContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.container; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.WarContainer; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; public class WarContainerFactoryProvider implements ContainerFactory { @Override public String getType() { return "war"; } @Override public boolean accept(API api, Path rootPath) { if (rootPath.toUri().toString().toLowerCase().endsWith(".war!/")) { return true; } else { // Extension: accept uncompressed WAR file containing a folder 'WEB-INF' try { return rootPath.getFileSystem().provider().getScheme().equals("file") && Files.exists(rootPath.resolve("WEB-INF")); } catch (InvalidPathException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } @Override public Container make(API api, Container.Entry parentEntry, Path rootPath) { return new WarContainer(api, parentEntry, rootPath); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/AarFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class AarFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "aar" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Android archive files (*.aar)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".aar"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.TreeNodeData; import org.jd.gui.spi.ContainerFactory; import org.jd.gui.spi.FileLoader; import org.jd.gui.spi.PanelFactory; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import java.io.*; import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; public abstract class AbstractFileLoaderProvider implements FileLoader { protected T load(API api, File file, Path rootPath) { ContainerEntry parentEntry = new ContainerEntry(file); ContainerFactory containerFactory = api.getContainerFactory(rootPath); if (containerFactory != null) { Container container = containerFactory.make(api, parentEntry, rootPath); if (container != null) { parentEntry.setChildren(container.getRoot().getChildren()); PanelFactory panelFactory = api.getMainPanelFactory(container); if (panelFactory != null) { T mainPanel = panelFactory.make(api, container); if (mainPanel != null) { TreeNodeFactory treeNodeFactory = api.getTreeNodeFactory(parentEntry); Object data = (treeNodeFactory != null) ? treeNodeFactory.make(api, parentEntry).getUserObject() : null; Icon icon = (data instanceof TreeNodeData) ? ((TreeNodeData)data).getIcon() : null; String location = file.getPath(); api.addPanel(file.getName(), icon, "Location: " + location, mainPanel); return mainPanel; } } } } return null; } protected static class ContainerEntry implements Container.Entry { protected static final Container PARENT_CONTAINER = new Container() { @Override public String getType() { return "generic"; } @Override public Container.Entry getRoot() { return null; } }; protected Collection children = Collections.emptyList(); protected File file; protected URI uri; protected String path; public ContainerEntry(File file) { this.file = file; this.uri = file.toURI(); this.path = uri.getPath(); if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } } @Override public Container getContainer() { return PARENT_CONTAINER; } @Override public Container.Entry getParent() { return null; } @Override public URI getUri() { return uri; } @Override public String getPath() { return path; } @Override public boolean isDirectory() { return file.isDirectory(); } @Override public long length() { return file.length(); } @Override public Collection getChildren() { return children; } @Override public InputStream getInputStream() { try { return new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { assert ExceptionUtil.printStackTrace(e); return null; } } public void setChildren(Collection children) { this.children = children; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.api.feature.UriOpenable; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Paths; public abstract class AbstractTypeFileLoaderProvider extends AbstractFileLoaderProvider { protected boolean load(API api, File file, String pathInFile) { // Search root path String pathSuffix = pathInFile; String path = file.getPath(); while (! path.endsWith(pathSuffix)) { int index = pathSuffix.indexOf(File.separator); if (index == -1) { pathSuffix = ""; } else { pathSuffix = pathSuffix.substring(index+1); } } if (! pathSuffix.isEmpty()) { // Init root file File rootFile = file; int index = pathSuffix.indexOf(File.separator); while (index != -1) { rootFile = rootFile.getParentFile(); pathSuffix = pathSuffix.substring(index+1); index = pathSuffix.indexOf(File.separator); } rootFile = rootFile.getParentFile(); // Create panel JComponent mainPanel = load(api, rootFile, Paths.get(rootFile.toURI())); if (mainPanel instanceof UriOpenable) { try { // Open page pathSuffix = file.getAbsolutePath().substring(rootFile.getAbsolutePath().length()).replace(File.separator, "/"); URI rootUri = rootFile.toURI(); URI uri = new URI(rootUri.getScheme(), rootUri.getHost(), rootUri.getPath() + '!' + pathSuffix, null); ((UriOpenable)mainPanel).openUri(uri); return true; } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } else { return mainPanel != null; } } return false; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.util.exception.ExceptionUtil; import org.objectweb.asm.ClassReader; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class ClassFileLoaderProvider extends AbstractTypeFileLoaderProvider { protected static final String[] EXTENSIONS = { "class" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Class files (*.class)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".class"); } @Override public boolean load(API api, File file) { try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { ClassReader classReader = new ClassReader(bis); String pathInFile = classReader.getClassName().replace("/", File.separator) + ".class"; return load(api, file, pathInFile); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); return false; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class EarFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "ear" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Enterprise application archive files (*.ear)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".ear"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class JarFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "jar" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Java archive files (*.jar)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".jar"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.util.io.TextReader; import java.io.File; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JavaFileLoaderProvider extends AbstractTypeFileLoaderProvider { protected static final String[] EXTENSIONS = { "java" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Java files (*.java)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".java"); } @Override public boolean load(API api, File file) { String text = TextReader.getText(file); Pattern pattern = Pattern.compile("(?s)(.*\\s)?package\\s+(\\S+)\\s*;.*"); Matcher matcher = pattern.matcher(text); if (matcher.matches()) { // Package name found String pathInFile = matcher.group(2).replace(".", File.separator) + File.separator + file.getName(); return load(api, file, pathInFile); } else { // Package name not found return load(api, file, file.getName()); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/JavaModuleFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class JavaModuleFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "jmod" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Java module files (*.jmod)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".jmod"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/KarFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class KarFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "kar" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Karaf archive files (*.kar)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".kar"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.util.io.TextReader; import org.jd.gui.view.component.LogPage; import java.io.File; public class LogFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "log" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Log files (*.log)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".log"); } @Override public boolean load(API api, File file) { api.addPanel(file.getName(), null, "Location: " + file.getAbsolutePath(), new LogPage(api, file.toURI(), TextReader.getText(file))); return true; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import java.io.File; public class WarFileLoaderProvider extends ZipFileLoaderProvider { protected static final String[] EXTENSIONS = { "war" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Web application archive files (*.war)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".war"); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.fileloader; import org.jd.gui.api.API; import org.jd.gui.util.exception.ExceptionUtil; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Collections; import java.util.Iterator; public class ZipFileLoaderProvider extends AbstractFileLoaderProvider { protected static final String[] EXTENSIONS = { "zip" }; @Override public String[] getExtensions() { return EXTENSIONS; } @Override public String getDescription() { return "Zip files (*.zip)"; } @Override public boolean accept(API api, File file) { return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(".zip"); } @Override public boolean load(API api, File file) { try { URI fileUri = file.toURI(); URI uri = new URI("jar:" + fileUri.getScheme(), fileUri.getHost(), fileUri.getPath() + "!/", null); FileSystem fileSystem; try { fileSystem = FileSystems.getFileSystem(uri); } catch (FileSystemNotFoundException e) { fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()); } if (fileSystem != null) { Iterator rootDirectories = fileSystem.getRootDirectories().iterator(); if (rootDirectories.hasNext()) { return load(api, file, rootDirectories.next()) != null; } } } catch (URISyntaxException|IOException e) { assert ExceptionUtil.printStackTrace(e); } return false; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.Indexer; import org.jd.gui.util.exception.ExceptionUtil; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.regex.Pattern; public abstract class AbstractIndexerProvider implements Indexer { protected List externalSelectors; protected Pattern externalPathPattern; /** * Initialize "selectors" and "pathPattern" with optional external properties file */ public AbstractIndexerProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".properties")) { if (is != null) { properties.load(is); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } init(properties); } protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); if (selectors != null) { externalSelectors = Arrays.asList(selectors.split(",")); } String pathRegExp = properties.getProperty("pathRegExp"); if (pathRegExp != null) { externalPathPattern = Pattern.compile(pathRegExp); } } protected String[] appendSelectors(String selector) { if (externalSelectors == null) { return new String[] { selector }; } else { int size = externalSelectors.size(); String[] array = new String[size+1]; externalSelectors.toArray(array); array[size] = selector; return array; } } protected String[] appendSelectors(String... selectors) { if (externalSelectors == null) { return selectors; } else { int size = externalSelectors.size(); String[] array = new String[size+selectors.length]; externalSelectors.toArray(array); System.arraycopy(selectors, 0, array, size, selectors.length); return array; } } @Override public Pattern getPathPattern() { return externalPathPattern; } @SuppressWarnings("unchecked") protected static void addToIndexes(Indexes indexes, String indexName, Set set, Container.Entry entry) { if (set.size() > 0) { Map index = indexes.getIndex(indexName); for (String key : set) { index.get(key).add(entry); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureVisitor; import java.io.InputStream; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.regex.Pattern; import static org.objectweb.asm.ClassReader.*; /** * Unsafe thread implementation of class file indexer. */ public class ClassFileIndexerProvider extends AbstractIndexerProvider { protected HashSet typeDeclarationSet = new HashSet<>(); protected HashSet constructorDeclarationSet = new HashSet<>(); protected HashSet methodDeclarationSet = new HashSet<>(); protected HashSet fieldDeclarationSet = new HashSet<>(); protected HashSet typeReferenceSet = new HashSet<>(); protected HashSet constructorReferenceSet = new HashSet<>(); protected HashSet methodReferenceSet = new HashSet<>(); protected HashSet fieldReferenceSet = new HashSet<>(); protected HashSet stringSet = new HashSet<>(); protected HashSet superTypeNameSet = new HashSet<>(); protected HashSet descriptorSet = new HashSet<>(); protected ClassIndexer classIndexer = new ClassIndexer(); protected SignatureIndexer signatureIndexer = new SignatureIndexer(); @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("^((?!module-info\\.class).)*$"); } else { return externalPathPattern; } } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { // Cleaning sets... typeDeclarationSet.clear(); constructorDeclarationSet.clear(); methodDeclarationSet.clear(); fieldDeclarationSet.clear(); typeReferenceSet.clear(); constructorReferenceSet.clear(); methodReferenceSet.clear(); fieldReferenceSet.clear(); stringSet.clear(); superTypeNameSet.clear(); descriptorSet.clear(); try (InputStream inputStream = entry.getInputStream()) { // Index field, method, interfaces & super type ClassReader classReader = new ClassReader(inputStream); classReader.accept(classIndexer, SKIP_CODE|SKIP_DEBUG|SKIP_FRAMES); // Index descriptors for (String descriptor : descriptorSet) { new SignatureReader(descriptor).accept(signatureIndexer); } // Index references char[] buffer = new char[classReader.getMaxStringLength()]; for (int i=classReader.getItemCount()-1; i>0; i--) { int startIndex = classReader.getItem(i); if (startIndex != 0) { int tag = classReader.readByte(startIndex-1); switch (tag) { case 7: // CONSTANT_Class String className = classReader.readUTF8(startIndex, buffer); if (className.startsWith("[")) { new SignatureReader(className).acceptType(signatureIndexer); } else { typeReferenceSet.add(className); } break; case 8: // CONSTANT_String String str = classReader.readUTF8(startIndex, buffer); stringSet.add(str); break; case 9: // CONSTANT_Fieldref int nameAndTypeItem = classReader.readUnsignedShort(startIndex+2); int nameAndTypeIndex = classReader.getItem(nameAndTypeItem); tag = classReader.readByte(nameAndTypeIndex-1); if (tag == 12) { // CONSTANT_NameAndType String fieldName = classReader.readUTF8(nameAndTypeIndex, buffer); fieldReferenceSet.add(fieldName); } break; case 10: // CONSTANT_Methodref: case 11: // CONSTANT_InterfaceMethodref: nameAndTypeItem = classReader.readUnsignedShort(startIndex+2); nameAndTypeIndex = classReader.getItem(nameAndTypeItem); tag = classReader.readByte(nameAndTypeIndex-1); if (tag == 12) { // CONSTANT_NameAndType String methodName = classReader.readUTF8(nameAndTypeIndex, buffer); if ("".equals(methodName)) { int classItem = classReader.readUnsignedShort(startIndex); int classIndex = classReader.getItem(classItem); className = classReader.readUTF8(classIndex, buffer); constructorReferenceSet.add(className); } else { methodReferenceSet.add(methodName); } } break; } } } String typeName = classIndexer.name; // Append sets to indexes addToIndexes(indexes, "typeDeclarations", typeDeclarationSet, entry); addToIndexes(indexes, "constructorDeclarations", constructorDeclarationSet, entry); addToIndexes(indexes, "methodDeclarations", methodDeclarationSet, entry); addToIndexes(indexes, "fieldDeclarations", fieldDeclarationSet, entry); addToIndexes(indexes, "typeReferences", typeReferenceSet, entry); addToIndexes(indexes, "constructorReferences", constructorReferenceSet, entry); addToIndexes(indexes, "methodReferences", methodReferenceSet, entry); addToIndexes(indexes, "fieldReferences", fieldReferenceSet, entry); addToIndexes(indexes, "strings", stringSet, entry); // Populate map [super type name : [sub type name]] if (superTypeNameSet.size() > 0) { Map index = indexes.getIndex("subTypeNames"); for (String superTypeName : superTypeNameSet) { index.get(superTypeName).add(typeName); } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } protected class ClassIndexer extends ClassVisitor { protected AnnotationIndexer annotationIndexer = new AnnotationIndexer(); protected FieldIndexer fieldIndexer = new FieldIndexer(annotationIndexer); protected MethodIndexer methodIndexer = new MethodIndexer(annotationIndexer); protected String name; public ClassIndexer() { super(Opcodes.ASM7); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.name = name; typeDeclarationSet.add(name); if (superName != null) { superTypeNameSet.add(superName); } if (interfaces != null) { for (int i=interfaces.length-1; i>=0; i--) { superTypeNameSet.add(interfaces[i]); } } } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { fieldDeclarationSet.add(name); descriptorSet.add(signature==null ? desc : signature); return fieldIndexer; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ("".equals(name)) { constructorDeclarationSet.add(this.name); } else if (! "".equals(name)) { methodDeclarationSet.add(name); } descriptorSet.add(signature==null ? desc : signature); if (exceptions != null) { for (int i=exceptions.length-1; i>=0; i--) { typeReferenceSet.add(exceptions[i]); } } return methodIndexer; } } protected class SignatureIndexer extends SignatureVisitor { SignatureIndexer() { super(Opcodes.ASM7); } @Override public void visitClassType(String name) { typeReferenceSet.add(name); } } protected class AnnotationIndexer extends AnnotationVisitor { public AnnotationIndexer() { super(Opcodes.ASM7); } @Override public void visitEnum(String name, String desc, String value) { descriptorSet.add(desc); } @Override public AnnotationVisitor visitAnnotation(String name, String desc) { descriptorSet.add(desc); return this; } } protected class FieldIndexer extends FieldVisitor { protected AnnotationIndexer annotationIndexer; public FieldIndexer(AnnotationIndexer annotationIndexer) { super(Opcodes.ASM7); this.annotationIndexer = annotationIndexer; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } } protected class MethodIndexer extends MethodVisitor { protected AnnotationIndexer annotationIndexer; public MethodIndexer(AnnotationIndexer annotationIndexer) { super(Opcodes.ASM7); this.annotationIndexer = annotationIndexer; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { descriptorSet.add(desc); return annotationIndexer; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.Indexer; public class DirectoryIndexerProvider extends AbstractIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:dir:*"); } @Override public void index(API api, Container.Entry entry, Indexes indexes) { int depth = 15; try { depth = Integer.valueOf(api.getPreferences().get("DirectoryIndexerPreferences.maximumDepth")); } catch (NumberFormatException ignore) { } index(api, entry, indexes, depth); } public void index(API api, Container.Entry entry, Indexes indexes, int depth) { if (depth-- > 0) { for (Container.Entry e : entry.getChildren()) { if (e.isDirectory()) { index(api, e, indexes, depth); } else { Indexer indexer = api.getIndexer(e); if (indexer != null) { indexer.index(api, e, indexes); } } } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.io.TextReader; import org.jd.gui.util.xml.AbstractXmlPathFinder; import java.util.Arrays; import java.util.Collection; import java.util.Map; public class EjbJarXmlFileIndexerProvider extends XmlBasedFileIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:META-INF/ejb-jar.xml"); } @Override public void index(API api, Container.Entry entry, Indexes indexes) { super.index(api, entry, indexes); new EjbJarXmlPathFinder(entry, indexes).find(TextReader.getText(entry.getInputStream())); } public static class EjbJarXmlPathFinder extends AbstractXmlPathFinder { protected Container.Entry entry; protected Map index; public EjbJarXmlPathFinder(Container.Entry entry, Indexes indexes) { super(Arrays.asList( "ejb-jar/assembly-descriptor/application-exception/exception-class", "ejb-jar/assembly-descriptor/interceptor-binding/interceptor-class", "ejb-jar/enterprise-beans/entity/home", "ejb-jar/enterprise-beans/entity/remote", "ejb-jar/enterprise-beans/entity/ejb-class", "ejb-jar/enterprise-beans/entity/prim-key-class", "ejb-jar/enterprise-beans/message-driven/ejb-class", "ejb-jar/enterprise-beans/message-driven/messaging-type", "ejb-jar/enterprise-beans/message-driven/resource-ref/injection-target/injection-target-class", "ejb-jar/enterprise-beans/message-driven/resource-env-ref/injection-target/injection-target-class", "ejb-jar/enterprise-beans/session/home", "ejb-jar/enterprise-beans/session/local", "ejb-jar/enterprise-beans/session/remote", "ejb-jar/enterprise-beans/session/business-local", "ejb-jar/enterprise-beans/session/business-remote", "ejb-jar/enterprise-beans/session/service-endpoint", "ejb-jar/enterprise-beans/session/ejb-class", "ejb-jar/enterprise-beans/session/ejb-ref/home", "ejb-jar/enterprise-beans/session/ejb-ref/remote", "ejb-jar/interceptors/interceptor/around-invoke/class", "ejb-jar/interceptors/interceptor/ejb-ref/home", "ejb-jar/interceptors/interceptor/ejb-ref/remote", "ejb-jar/interceptors/interceptor/interceptor-class" )); this.entry = entry; this.index = indexes.getIndex("typeReferences"); } @Override @SuppressWarnings("unchecked") public void handle(String path, String text, int position) { index.get(text.replace(".", "/")).add(entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.parser.antlr.ANTLRJavaParser; import org.jd.gui.util.parser.antlr.AbstractJavaListener; import org.jd.gui.util.parser.antlr.JavaParser; import java.io.IOException; import java.io.InputStream; import java.util.*; /** * Unsafe thread implementation of java file indexer. */ public class JavaFileIndexerProvider extends AbstractIndexerProvider { static { // Early class loading ANTLRJavaParser.parse(new ANTLRInputStream("class EarlyLoading{}"), new Listener(null)); } @Override public String[] getSelectors() { return appendSelectors("*:file:*.java"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { try (InputStream inputStream = entry.getInputStream()) { Listener listener = new Listener(entry); ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener); // Append sets to indexes addToIndexes(indexes, "typeDeclarations", listener.getTypeDeclarationSet(), entry); addToIndexes(indexes, "constructorDeclarations", listener.getConstructorDeclarationSet(), entry); addToIndexes(indexes, "methodDeclarations", listener.getMethodDeclarationSet(), entry); addToIndexes(indexes, "fieldDeclarations", listener.getFieldDeclarationSet(), entry); addToIndexes(indexes, "typeReferences", listener.getTypeReferenceSet(), entry); addToIndexes(indexes, "constructorReferences", listener.getConstructorReferenceSet(), entry); addToIndexes(indexes, "methodReferences", listener.getMethodReferenceSet(), entry); addToIndexes(indexes, "fieldReferences", listener.getFieldReferenceSet(), entry); addToIndexes(indexes, "strings", listener.getStringSet(), entry); // Populate map [super type name : [sub type name]] Map index = indexes.getIndex("subTypeNames"); for (Map.Entry> e : listener.getSuperTypeNamesMap().entrySet()) { String typeName = e.getKey(); for (String superTypeName : e.getValue()) { index.get(superTypeName).add(typeName); } } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } protected static class Listener extends AbstractJavaListener { protected HashSet typeDeclarationSet = new HashSet<>(); protected HashSet constructorDeclarationSet = new HashSet<>(); protected HashSet methodDeclarationSet = new HashSet<>(); protected HashSet fieldDeclarationSet = new HashSet<>(); protected HashSet typeReferenceSet = new HashSet<>(); protected HashSet constructorReferenceSet = new HashSet<>(); protected HashSet methodReferenceSet = new HashSet<>(); protected HashSet fieldReferenceSet = new HashSet<>(); protected HashSet stringSet = new HashSet<>(); protected HashMap> superTypeNamesMap = new HashMap<>(); protected StringBuilder sbTypeDeclaration = new StringBuilder(); public Listener(Container.Entry entry) { super(entry); } public HashSet getTypeDeclarationSet() { return typeDeclarationSet; } public HashSet getConstructorDeclarationSet() { return constructorDeclarationSet; } public HashSet getMethodDeclarationSet() { return methodDeclarationSet; } public HashSet getFieldDeclarationSet() { return fieldDeclarationSet; } public HashSet getTypeReferenceSet() { return typeReferenceSet; } public HashSet getConstructorReferenceSet() { return constructorReferenceSet; } public HashSet getMethodReferenceSet() { return methodReferenceSet; } public HashSet getFieldReferenceSet() { return fieldReferenceSet; } public HashSet getStringSet() { return stringSet; } public HashMap> getSuperTypeNamesMap() { return superTypeNamesMap; } // --- ANTLR Listener --- // public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { super.enterPackageDeclaration(ctx); if (! packageName.isEmpty()) { sbTypeDeclaration.append(packageName).append('/'); } } public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } protected void enterTypeDeclaration(ParserRuleContext ctx) { // Add type declaration TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0); if (identifier != null) { String typeName = identifier.getText(); int length = sbTypeDeclaration.length(); if ((length == 0) || (sbTypeDeclaration.charAt(length - 1) == '/')) { sbTypeDeclaration.append(typeName); } else { sbTypeDeclaration.append('$').append(typeName); } String internalTypeName = sbTypeDeclaration.toString(); typeDeclarationSet.add(internalTypeName); nameToInternalTypeName.put(typeName, internalTypeName); HashSet superInternalTypeNameSet = new HashSet<>(); // Add super type reference JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0); if (superType != null) { String superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier()); if (superQualifiedTypeName.charAt(0) != '*') superInternalTypeNameSet.add(superQualifiedTypeName); } // Add implementation references JavaParser.TypeListContext superInterfaces = ctx.getRuleContext(JavaParser.TypeListContext.class, 0); if (superInterfaces != null) { for (JavaParser.TypeContext superInterface : superInterfaces.type()) { String superQualifiedInterfaceName = resolveInternalTypeName(superInterface.classOrInterfaceType().Identifier()); if (superQualifiedInterfaceName.charAt(0) != '*') superInternalTypeNameSet.add(superQualifiedInterfaceName); } } if (!superInternalTypeNameSet.isEmpty()) { superTypeNamesMap.put(internalTypeName, superInternalTypeNameSet); } } } protected void exitTypeDeclaration() { int index = sbTypeDeclaration.lastIndexOf("$"); if (index == -1) { index = sbTypeDeclaration.lastIndexOf("/") + 1; } if (index == -1) { sbTypeDeclaration.setLength(0); } else { sbTypeDeclaration.setLength(index); } } public void enterType(JavaParser.TypeContext ctx) { // Add type reference JavaParser.ClassOrInterfaceTypeContext classOrInterfaceType = ctx.classOrInterfaceType(); if (classOrInterfaceType != null) { String internalTypeName = resolveInternalTypeName(classOrInterfaceType.Identifier()); if (internalTypeName.charAt(0) != '*') typeReferenceSet.add(internalTypeName); } } public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) { String name = constantDeclaratorContext.Identifier().getText(); fieldDeclarationSet.add(name); } } public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { TerminalNode identifier = declaration.variableDeclaratorId().Identifier(); if (identifier != null) { String name = identifier.getText(); fieldDeclarationSet.add(name); } } } public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { TerminalNode identifier = ctx.Identifier(); if (identifier != null) { String name = identifier.getText(); methodDeclarationSet.add(name); } } public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { TerminalNode identifier = ctx.Identifier(); if (identifier != null) { String name = identifier.getText(); methodDeclarationSet.add(name); } } public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { String name = ctx.Identifier().getText(); constructorDeclarationSet.add(name); } public void enterCreatedName(JavaParser.CreatedNameContext ctx) { String internalTypeName = resolveInternalTypeName(ctx.Identifier()); if ((internalTypeName != null) && (internalTypeName.charAt(0) != '*')) constructorReferenceSet.add(internalTypeName); } public void enterExpression(JavaParser.ExpressionContext ctx) { switch (ctx.getChildCount()) { case 3: if (getToken(ctx.children, 1, JavaParser.DOT) != null) { // Search "expression '.' Identifier" : field TerminalNode identifier3 = getToken(ctx.children, 2, JavaParser.Identifier); if (identifier3 != null) { String fieldName = identifier3.getText(); fieldReferenceSet.add(fieldName); } } else if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) { // Search "expression '(' ')'" : method if (getToken(ctx.children, 2, JavaParser.RPAREN) != null) { TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0)); if (identifier0 != null) { String methodName = identifier0.getText(); methodReferenceSet.add(methodName); } } } break; case 4: if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) { // Search "expression '(' expressionList ')'" : method if (getToken(ctx.children, 3, JavaParser.RPAREN) != null) { JavaParser.ExpressionListContext expressionListContext = ctx.expressionList(); if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) { TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0)); if (identifier0 != null) { String methodName = identifier0.getText(); methodReferenceSet.add(methodName); } } } } break; } } protected TerminalNode getToken(List children, int i, int type) { ParseTree pt = children.get(i); if (pt instanceof TerminalNode) { if (((TerminalNode)pt).getSymbol().getType() == type) { return (TerminalNode)pt; } } return null; } protected TerminalNode getRightTerminalNode(ParseTree pt) { if (pt instanceof ParserRuleContext) { List children = ((ParserRuleContext)pt).children; if (children != null) { int size = children.size(); if (size > 0) { ParseTree last = children.get(size - 1); if (last instanceof TerminalNode) { return (TerminalNode) last; } else { return getRightTerminalNode(last); } } } } return null; } public void enterLiteral(JavaParser.LiteralContext ctx) { TerminalNode stringLiteral = ctx.StringLiteral(); if (stringLiteral != null) { stringSet.add(stringLiteral.getSymbol().getText()); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/JavaModuleFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.Indexer; import java.util.Collection; import java.util.Map; public class JavaModuleFileIndexerProvider extends AbstractIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*.jmod"); } @Override public void index(API api, Container.Entry entry, Indexes indexes) { for (Container.Entry e : entry.getChildren()) { if (e.isDirectory() && e.getPath().equals("classes")) { Map packageDeclarationIndex = indexes.getIndex("packageDeclarations"); // Index module-info, packages and CLASS files index(api, e, indexes, packageDeclarationIndex); break; } } } @SuppressWarnings("unchecked") protected static void index(API api, Container.Entry entry, Indexes indexes, Map packageDeclarationIndex) { for (Container.Entry e : entry.getChildren()) { if (e.isDirectory()) { String path = e.getPath(); if (!path.startsWith("classes/META-INF")) { packageDeclarationIndex.get(path.substring(8)).add(e); // 8 = "classes/".length() } index(api, e, indexes, packageDeclarationIndex); } else { Indexer indexer = api.getIndexer(e); if (indexer != null) { indexer.index(api, e, indexes); } } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/JavaModuleInfoFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.objectweb.asm.*; import java.io.InputStream; import java.util.HashSet; import static org.objectweb.asm.ClassReader.*; /** * Unsafe thread implementation of class file indexer. */ public class JavaModuleInfoFileIndexerProvider extends AbstractIndexerProvider { protected HashSet javaModuleDeclarationSet = new HashSet<>(); protected HashSet javaModuleReferenceSet = new HashSet<>(); protected HashSet typeReferenceSet = new HashSet<>(); protected ClassIndexer classIndexer = new ClassIndexer(); @Override public String[] getSelectors() { return appendSelectors("jmod:file:classes/module-info.class"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { // Cleaning sets... javaModuleDeclarationSet.clear(); javaModuleReferenceSet.clear(); typeReferenceSet.clear(); try (InputStream inputStream = entry.getInputStream()) { // Index field, method, interfaces & super type ClassReader classReader = new ClassReader(inputStream); classReader.accept(classIndexer, SKIP_CODE|SKIP_DEBUG|SKIP_FRAMES); // Append sets to indexes addToIndexes(indexes, "javaModuleDeclarations", javaModuleDeclarationSet, entry); addToIndexes(indexes, "javaModuleReferences", javaModuleReferenceSet, entry); addToIndexes(indexes, "typeReferences", typeReferenceSet, entry); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } protected class ClassIndexer extends ClassVisitor { protected ModuleIndexer moduleIndexer = new ModuleIndexer(); public ClassIndexer() { super(Opcodes.ASM7); } @Override public ModuleVisitor visitModule(String moduleName, int moduleFlags, String moduleVersion) { javaModuleDeclarationSet.add(moduleName); return moduleIndexer; } } protected class ModuleIndexer extends ModuleVisitor { public ModuleIndexer() { super(Opcodes.ASM7); } @Override public void visitMainClass(final String mainClass) { typeReferenceSet.add(mainClass); } @Override public void visitRequire(final String module, final int access, final String version) { javaModuleReferenceSet.add(module); } @Override public void visitUse(final String service) { typeReferenceSet.add(service); } @Override public void visitExport(final String packaze, final int access, final String... modules) { if (modules != null) { for (String module : modules) { javaModuleReferenceSet.add(module); } } } @Override public void visitOpen(final String packaze, final int access, final String... modules) { if (modules != null) { for (String module : modules) { javaModuleReferenceSet.add(module); } } } @Override public void visitProvide(final String service, final String... providers) { typeReferenceSet.add(service); if (providers != null) { for (String provider : providers) { typeReferenceSet.add(provider); } } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Collection; import java.util.Map; import java.util.regex.Pattern; public class MetainfServiceFileIndexerProvider extends AbstractIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*"); } @Override public Pattern getPathPattern() { return (externalPathPattern != null) ? externalPathPattern : Pattern.compile("META-INF\\/services\\/[^\\/]+"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { Map index = indexes.getIndex("typeReferences"); try (BufferedReader br = new BufferedReader(new InputStreamReader(entry.getInputStream()))) { String line; while ((line = br.readLine()) != null) { String trim = line.trim(); if (!trim.isEmpty() && (trim.charAt(0) != '#')) { String internalTypeName = trim.replace(".", "/"); index.get(internalTypeName).add(entry); } } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.io.TextReader; public class TextFileIndexerProvider extends AbstractIndexerProvider { @Override public String[] getSelectors() { return appendSelectors( "*:file:*.txt", "*:file:*.html", "*:file:*.xhtml", "*:file:*.js", "*:file:*.jsp", "*:file:*.jspf", "*:file:*.xml", "*:file:*.xsl", "*:file:*.xslt", "*:file:*.xsd", "*:file:*.properties", "*:file:*.sql", "*:file:*.yaml", "*:file:*.yml", "*:file:*.json"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { indexes.getIndex("strings").get(TextReader.getText(entry.getInputStream())).add(entry); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.io.TextReader; import org.jd.gui.util.xml.AbstractXmlPathFinder; import java.util.Arrays; import java.util.Collection; import java.util.Map; public class WebXmlFileIndexerProvider extends XmlBasedFileIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:WEB-INF/web.xml"); } @Override public void index(API api, Container.Entry entry, Indexes indexes) { super.index(api, entry, indexes); new WebXmlPathFinder(entry, indexes).find(TextReader.getText(entry.getInputStream())); } protected static class WebXmlPathFinder extends AbstractXmlPathFinder { Container.Entry entry; Map index; public WebXmlPathFinder(Container.Entry entry, Indexes indexes) { super(Arrays.asList( "web-app/filter/filter-class", "web-app/listener/listener-class", "web-app/servlet/servlet-class" )); this.entry = entry; this.index = indexes.getIndex("typeReferences"); } @Override @SuppressWarnings("unchecked") public void handle(String path, String text, int position) { index.get(text.replace(".", "/")).add(entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.util.Collection; import java.util.HashSet; import java.util.Map; public class XmlBasedFileIndexerProvider extends AbstractIndexerProvider { protected XMLInputFactory factory; public XmlBasedFileIndexerProvider() { factory = XMLInputFactory.newInstance(); factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); } @Override public String[] getSelectors() { return appendSelectors("*:file:*.xsl", "*:file:*.xslt", "*:file:*.xsd"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { HashSet stringSet = new HashSet<>(); XMLStreamReader reader = null; try { reader = factory.createXMLStreamReader(entry.getInputStream()); stringSet.add(reader.getVersion()); stringSet.add(reader.getEncoding()); stringSet.add(reader.getCharacterEncodingScheme()); while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: stringSet.add(reader.getLocalName()); for (int i = reader.getAttributeCount() - 1; i >= 0; i--) { stringSet.add(reader.getAttributeLocalName(i)); stringSet.add(reader.getAttributeValue(i)); } for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) { stringSet.add(reader.getNamespacePrefix(i)); stringSet.add(reader.getNamespaceURI(i)); } break; case XMLStreamConstants.PROCESSING_INSTRUCTION: stringSet.add(reader.getPITarget()); stringSet.add(reader.getPIData()); break; case XMLStreamConstants.START_DOCUMENT: stringSet.add(reader.getVersion()); stringSet.add(reader.getEncoding()); stringSet.add(reader.getCharacterEncodingScheme()); break; case XMLStreamConstants.ENTITY_REFERENCE: stringSet.add(reader.getLocalName()); stringSet.add(reader.getText()); break; case XMLStreamConstants.ATTRIBUTE: stringSet.add(reader.getPrefix()); stringSet.add(reader.getNamespaceURI()); stringSet.add(reader.getLocalName()); stringSet.add(reader.getText()); break; case XMLStreamConstants.COMMENT: case XMLStreamConstants.DTD: case XMLStreamConstants.CDATA: case XMLStreamConstants.CHARACTERS: stringSet.add(reader.getText().trim()); break; case XMLStreamConstants.NAMESPACE: for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) { stringSet.add(reader.getNamespacePrefix(i)); stringSet.add(reader.getNamespaceURI(i)); } break; } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } finally { if (reader != null) { try { reader.close(); } catch (XMLStreamException e) { assert ExceptionUtil.printStackTrace(e); } } } Map stringIndex = indexes.getIndex("strings"); for (String string : stringSet) { if ((string != null) && !string.isEmpty()) { stringIndex.get(string).add(entry); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.util.Collection; import java.util.HashSet; import java.util.Map; public class XmlFileIndexerProvider extends AbstractIndexerProvider { protected XMLInputFactory factory; public XmlFileIndexerProvider() { factory = XMLInputFactory.newInstance(); factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); } @Override public String[] getSelectors() { return appendSelectors("*:file:*.xml"); } @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { HashSet stringSet = new HashSet<>(); HashSet typeReferenceSet = new HashSet<>(); XMLStreamReader reader = null; try { reader = factory.createXMLStreamReader(entry.getInputStream()); stringSet.add(reader.getVersion()); stringSet.add(reader.getEncoding()); stringSet.add(reader.getCharacterEncodingScheme()); while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: boolean beanFlag = reader.getLocalName().equals("bean"); stringSet.add(reader.getLocalName()); for (int i = reader.getAttributeCount() - 1; i >= 0; i--) { String attributeName = reader.getAttributeLocalName(i); stringSet.add(attributeName); if (beanFlag && attributeName.equals("class")) { // String bean reference typeReferenceSet.add(reader.getAttributeValue(i).replace(".", "/")); } else { stringSet.add(reader.getAttributeValue(i)); } } for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) { stringSet.add(reader.getNamespacePrefix(i)); stringSet.add(reader.getNamespaceURI(i)); } break; case XMLStreamConstants.PROCESSING_INSTRUCTION: stringSet.add(reader.getPITarget()); stringSet.add(reader.getPIData()); break; case XMLStreamConstants.START_DOCUMENT: stringSet.add(reader.getVersion()); stringSet.add(reader.getEncoding()); stringSet.add(reader.getCharacterEncodingScheme()); break; case XMLStreamConstants.ENTITY_REFERENCE: stringSet.add(reader.getLocalName()); stringSet.add(reader.getText()); break; case XMLStreamConstants.ATTRIBUTE: stringSet.add(reader.getPrefix()); stringSet.add(reader.getNamespaceURI()); stringSet.add(reader.getLocalName()); stringSet.add(reader.getText()); break; case XMLStreamConstants.COMMENT: case XMLStreamConstants.DTD: case XMLStreamConstants.CDATA: case XMLStreamConstants.CHARACTERS: stringSet.add(reader.getText().trim()); break; case XMLStreamConstants.NAMESPACE: for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) { stringSet.add(reader.getNamespacePrefix(i)); stringSet.add(reader.getNamespaceURI(i)); } break; } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } finally { if (reader != null) { try { reader.close(); } catch (XMLStreamException e) { assert ExceptionUtil.printStackTrace(e); } } } Map stringIndex = indexes.getIndex("strings"); Map typeReferenceIndex = indexes.getIndex("typeReferences"); for (String string : stringSet) { if ((string != null) && !string.isEmpty()) { stringIndex.get(string).add(entry); } } for (String ref : typeReferenceSet) { if ((ref != null) && !ref.isEmpty()) { typeReferenceIndex.get(ref).add(entry); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.indexer; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.spi.Indexer; public class ZipFileIndexerProvider extends AbstractIndexerProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*.zip", "*:file:*.jar", "*:file:*.war", "*:file:*.ear", "*:file:*.aar", "*:file:*.kar"); } @Override public void index(API api, Container.Entry entry, Indexes indexes) { for (Container.Entry e : entry.getChildren()) { if (e.isDirectory()) { index(api, e, indexes); } else { Indexer indexer = api.getIndexer(e); if (indexer != null) { indexer.index(api, e, indexes); } } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.pastehandler; import org.jd.gui.api.API; import org.jd.gui.spi.PasteHandler; import org.jd.gui.view.component.LogPage; import java.net.URI; public class LogPasteHandler implements PasteHandler { protected static int counter = 0; public boolean accept(Object obj) { return obj instanceof String; } public void paste(API api, Object obj) { String title = "clipboard-" + (++counter) + ".log"; URI uri = URI.create("memory://" + title); String content = (obj == null) ? null : obj.toString(); api.addPanel(title, null, null, new LogPage(api, uri, content)); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileDecompilerPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.spi.PreferencesPanel; import javax.swing.*; import java.awt.*; import java.util.Map; public class ClassFileDecompilerPreferencesProvider extends JPanel implements PreferencesPanel { protected static final String ESCAPE_UNICODE_CHARACTERS = "ClassFileDecompilerPreferences.escapeUnicodeCharacters"; protected static final String REALIGN_LINE_NUMBERS = "ClassFileDecompilerPreferences.realignLineNumbers"; protected PreferencesPanel.PreferencesPanelChangeListener listener = null; protected JCheckBox escapeUnicodeCharactersCheckBox; protected JCheckBox realignLineNumbersCheckBox; public ClassFileDecompilerPreferencesProvider() { super(new GridLayout(0,1)); escapeUnicodeCharactersCheckBox = new JCheckBox("Escape unicode characters"); realignLineNumbersCheckBox = new JCheckBox("Realign line numbers"); add(escapeUnicodeCharactersCheckBox); add(realignLineNumbersCheckBox); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "Decompiler"; } @Override public String getPreferencesPanelTitle() { return "Class file"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) {} @Override public boolean isActivated() { return true; } @Override public void loadPreferences(Map preferences) { escapeUnicodeCharactersCheckBox.setSelected("true".equals(preferences.get(ESCAPE_UNICODE_CHARACTERS))); realignLineNumbersCheckBox.setSelected("true".equals(preferences.get(REALIGN_LINE_NUMBERS))); } @Override public void savePreferences(Map preferences) { preferences.put(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.isSelected())); preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.isSelected())); } @Override public boolean arePreferencesValid() { return true; } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} } ================================================ FILE: services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.spi.PreferencesPanel; import javax.swing.*; import java.awt.*; import java.util.Map; public class ClassFileSaverPreferencesProvider extends JPanel implements PreferencesPanel { protected static final String WRITE_LINE_NUMBERS = "ClassFileSaverPreferences.writeLineNumbers"; protected static final String WRITE_METADATA = "ClassFileSaverPreferences.writeMetadata"; protected JCheckBox writeLineNumbersCheckBox; protected JCheckBox writeMetadataCheckBox; public ClassFileSaverPreferencesProvider() { super(new GridLayout(0,1)); writeLineNumbersCheckBox = new JCheckBox("Write original line numbers"); writeMetadataCheckBox = new JCheckBox("Write metadata"); add(writeLineNumbersCheckBox); add(writeMetadataCheckBox); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "Source Saver"; } @Override public String getPreferencesPanelTitle() { return "Class file"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) {} @Override public boolean isActivated() { return true; } @Override public void loadPreferences(Map preferences) { writeLineNumbersCheckBox.setSelected(!"false".equals(preferences.get(WRITE_LINE_NUMBERS))); writeMetadataCheckBox.setSelected(!"false".equals(preferences.get(WRITE_METADATA))); } @Override public void savePreferences(Map preferences) { preferences.put(WRITE_LINE_NUMBERS, Boolean.toString(writeLineNumbersCheckBox.isSelected())); preferences.put(WRITE_METADATA, Boolean.toString(writeMetadataCheckBox.isSelected())); } @Override public boolean arePreferencesValid() { return true; } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} } ================================================ FILE: services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.spi.PreferencesPanel; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; import java.util.Map; public class DirectoryIndexerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener { protected static final int MAX_VALUE = 30; protected static final String MAXIMUM_DEPTH_KEY = "DirectoryIndexerPreferences.maximumDepth"; protected PreferencesPanel.PreferencesPanelChangeListener listener = null; protected JTextField maximumDepthTextField; protected Color errorBackgroundColor = Color.RED; protected Color defaultBackgroundColor; public DirectoryIndexerPreferencesProvider() { super(new BorderLayout()); add(new JLabel("Maximum depth (1.." + MAX_VALUE + "): "), BorderLayout.WEST); maximumDepthTextField = new JTextField(); maximumDepthTextField.getDocument().addDocumentListener(this); add(maximumDepthTextField, BorderLayout.CENTER); defaultBackgroundColor = maximumDepthTextField.getBackground(); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "Indexer"; } @Override public String getPreferencesPanelTitle() { return "Directory exploration"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) { this.errorBackgroundColor = errorBackgroundColor; } @Override public boolean isActivated() { return true; } @Override public void loadPreferences(Map preferences) { String preference = preferences.get(MAXIMUM_DEPTH_KEY); maximumDepthTextField.setText((preference != null) ? preference : "15"); maximumDepthTextField.setCaretPosition(maximumDepthTextField.getText().length()); } @Override public void savePreferences(Map preferences) { preferences.put(MAXIMUM_DEPTH_KEY, maximumDepthTextField.getText()); } @Override public boolean arePreferencesValid() { try { int i = Integer.valueOf(maximumDepthTextField.getText()); return (i > 0) && (i <= MAX_VALUE); } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); return false; } } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) { this.listener = listener; } // --- DocumentListener --- // @Override public void insertUpdate(DocumentEvent e) { onTextChange(); } @Override public void removeUpdate(DocumentEvent e) { onTextChange(); } @Override public void changedUpdate(DocumentEvent e) { onTextChange(); } public void onTextChange() { maximumDepthTextField.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor); if (listener != null) { listener.preferencesPanelChanged(this); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/preferencespanel/MavenOrgSourceLoaderPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.jd.gui.spi.PreferencesPanel; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Map; import java.util.regex.Pattern; public class MavenOrgSourceLoaderPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener, ActionListener { public static final String ACTIVATED = "MavenOrgSourceLoaderPreferencesProvider.activated"; public static final String FILTERS = "MavenOrgSourceLoaderPreferencesProvider.filters"; public static final String DEFAULT_FILTERS_VALUE = "+org +com.google +com.springsource +com.sun -com +java +javax +sun +sunw " + "+spring +springframework +springmodules +tomcat +maven +edu"; protected static final Pattern CONTROL_PATTERN = Pattern.compile("([+-][a-zA-Z_0-9$_.]+(\\s+[+-][a-zA-Z_0-9$_.]+)*)?\\s*"); protected JCheckBox enableCheckBox; protected JTextArea filtersTextArea; protected JButton resetButton; protected Color errorBackgroundColor = Color.RED; protected Color defaultBackgroundColor; protected PreferencesPanel.PreferencesPanelChangeListener listener; public MavenOrgSourceLoaderPreferencesProvider() { super(new BorderLayout()); enableCheckBox = new JCheckBox("Search source code on maven.org for:"); enableCheckBox.addActionListener(this); filtersTextArea = new JTextArea(); filtersTextArea.setFont(getFont()); filtersTextArea.setLineWrap(true); filtersTextArea.getDocument().addDocumentListener(this); defaultBackgroundColor = filtersTextArea.getBackground(); JComponent spacer = new JComponent() {}; JScrollPane scrollPane = new JScrollPane(filtersTextArea); String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("windows")) { spacer.setPreferredSize(new Dimension(22, -1)); scrollPane.setPreferredSize(new Dimension(-1, 50)); } else if (osName.contains("mac os")) { spacer.setPreferredSize(new Dimension(28, -1)); scrollPane.setPreferredSize(new Dimension(-1, 56)); } else { spacer.setPreferredSize(new Dimension(22, -1)); scrollPane.setPreferredSize(new Dimension(-1, 56)); } resetButton = new JButton("Reset"); resetButton.addActionListener(this); JPanel southPanel = new JPanel(new BorderLayout()); southPanel.add(resetButton, BorderLayout.EAST); add(enableCheckBox, BorderLayout.NORTH); add(spacer, BorderLayout.WEST); add(scrollPane, BorderLayout.CENTER); add(southPanel, BorderLayout.SOUTH); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "Source loader"; } @Override public String getPreferencesPanelTitle() { return "maven.org"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) { this.errorBackgroundColor = errorBackgroundColor; } @Override public boolean isActivated() { return true; } @Override public void loadPreferences(Map preferences) { boolean enabled = !"false".equals(preferences.get(ACTIVATED)); enableCheckBox.setSelected(enabled); filtersTextArea.setEnabled(enabled); resetButton.setEnabled(enabled); String filters = preferences.get(FILTERS); filtersTextArea.setText((filters == null) || filters.isEmpty() ? DEFAULT_FILTERS_VALUE : filters); } @Override public void savePreferences(Map preferences) { preferences.put(ACTIVATED, Boolean.toString(enableCheckBox.isSelected())); preferences.put(FILTERS, filtersTextArea.getText().trim()); } @Override public boolean arePreferencesValid() { return CONTROL_PATTERN.matcher(filtersTextArea.getText()).matches(); } @Override public void addPreferencesChangeListener(PreferencesPanelChangeListener listener) { this.listener = listener; } // --- DocumentListener --- // @Override public void insertUpdate(DocumentEvent e) { onTextChange(); } @Override public void removeUpdate(DocumentEvent e) { onTextChange(); } @Override public void changedUpdate(DocumentEvent e) { onTextChange(); } protected void onTextChange() { filtersTextArea.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor); if (listener != null) { listener.preferencesPanelChanged(this); } } // --- ActionListener --- // @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == enableCheckBox) { boolean enabled = enableCheckBox.isSelected(); filtersTextArea.setEnabled(enabled); resetButton.setEnabled(enabled); } else { // Reset button filtersTextArea.setText(DEFAULT_FILTERS_VALUE); filtersTextArea.requestFocus(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.preferencespanel; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.Theme; import org.jd.gui.spi.PreferencesPanel; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; import java.io.IOException; import java.util.Map; public class ViewerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener { protected static final int MIN_VALUE = 2; protected static final int MAX_VALUE = 40; protected static final String FONT_SIZE_KEY = "ViewerPreferences.fontSize"; protected PreferencesPanel.PreferencesPanelChangeListener listener = null; protected JTextField fontSizeTextField; protected Color errorBackgroundColor = Color.RED; protected Color defaultBackgroundColor; public ViewerPreferencesProvider() { super(new BorderLayout()); add(new JLabel("Font size (" + MIN_VALUE + ".." + MAX_VALUE + "): "), BorderLayout.WEST); fontSizeTextField = new JTextField(); fontSizeTextField.getDocument().addDocumentListener(this); add(fontSizeTextField, BorderLayout.CENTER); defaultBackgroundColor = fontSizeTextField.getBackground(); } // --- PreferencesPanel --- // @Override public String getPreferencesGroupTitle() { return "Viewer"; } @Override public String getPreferencesPanelTitle() { return "Appearance"; } @Override public JComponent getPanel() { return this; } @Override public void init(Color errorBackgroundColor) { this.errorBackgroundColor = errorBackgroundColor; } @Override public boolean isActivated() { return true; } @Override public void loadPreferences(Map preferences) { String fontSize = preferences.get(FONT_SIZE_KEY); if (fontSize == null) { // Search default value for the current platform RSyntaxTextArea textArea = new RSyntaxTextArea(); try { Theme theme = Theme.load(getClass().getClassLoader().getResourceAsStream("rsyntaxtextarea/themes/eclipse.xml")); theme.apply(textArea); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } fontSize = String.valueOf(textArea.getFont().getSize()); } fontSizeTextField.setText(fontSize); fontSizeTextField.setCaretPosition(fontSizeTextField.getText().length()); } @Override public void savePreferences(Map preferences) { preferences.put(FONT_SIZE_KEY, fontSizeTextField.getText()); } @Override public boolean arePreferencesValid() { try { int i = Integer.valueOf(fontSizeTextField.getText()); return (i >= MIN_VALUE) && (i <= MAX_VALUE); } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); return false; } } @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) { this.listener = listener; } // --- DocumentListener --- // @Override public void insertUpdate(DocumentEvent e) { onTextChange(); } @Override public void removeUpdate(DocumentEvent e) { onTextChange(); } @Override public void changedUpdate(DocumentEvent e) { onTextChange(); } public void onTextChange() { fontSizeTextField.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor); if (listener != null) { listener.preferencesPanelChanged(this); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourceloader/MavenOrgSourceLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourceloader; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider; import org.jd.gui.spi.SourceLoader; import org.jd.gui.util.exception.ExceptionUtil; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamReader; import java.io.*; import java.net.URL; import java.security.DigestInputStream; import java.security.MessageDigest; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class MavenOrgSourceLoaderProvider implements SourceLoader { protected static final String MAVENORG_SEARCH_URL_PREFIX = "https://search.maven.org/solrsearch/select?q=1:%22"; protected static final String MAVENORG_SEARCH_URL_SUFFIX = "%22&rows=20&wt=xml"; protected static final String MAVENORG_LOAD_URL_PREFIX = "https://search.maven.org/classic/remotecontent?filepath="; protected static final String MAVENORG_LOAD_URL_SUFFIX = "-sources.jar"; protected HashSet failed = new HashSet<>(); protected HashMap cache = new HashMap<>(); @Override public String getSource(API api, Container.Entry entry) { if (isActivated(api)) { String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS); if ((filters == null) || filters.isEmpty()) { filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE; } if (accepted(filters, entry.getPath())) { return searchSource(entry, cache.get(entry.getContainer().getRoot().getParent())); } } return null; } @Override public String loadSource(API api, Container.Entry entry) { if (isActivated(api)) { String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS); if ((filters == null) || filters.isEmpty()) { filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE; } if (accepted(filters, entry.getPath())) { return searchSource(entry, downloadSourceJarFile(entry.getContainer().getRoot().getParent())); } } return null; } @Override public File loadSourceFile(API api, Container.Entry entry) { return isActivated(api) ? downloadSourceJarFile(entry) : null; } private static boolean isActivated(API api) { return !"false".equals(api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.ACTIVATED)); } protected String searchSource(Container.Entry entry, File sourceJarFile) { if (sourceJarFile != null) { byte[] buffer = new byte[1024 * 2]; try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceJarFile)))) { ZipEntry ze = zis.getNextEntry(); String name = entry.getPath(); name = name.substring(0, name.length()-6) + ".java"; // 6 = ".class".length() while (ze != null) { if (ze.getName().equals(name)) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int read = zis.read(buffer); while (read > 0) { out.write(buffer, 0, read); read = zis.read(buffer); } return new String(out.toByteArray(), "UTF-8"); } ze = zis.getNextEntry(); } zis.closeEntry(); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } return null; } protected File downloadSourceJarFile(Container.Entry entry) { if (cache.containsKey(entry)) { return cache.get(entry); } if (!entry.isDirectory() && !failed.contains(entry)) { try { // SHA-1 MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); byte[] buffer = new byte[1024 * 2]; try (DigestInputStream is = new DigestInputStream(entry.getInputStream(), messageDigest)) { while (is.read(buffer) > -1); } byte[] array = messageDigest.digest(); StringBuilder sb = new StringBuilder(); for (byte b : array) { sb.append(hexa((b & 255) >> 4)); sb.append(hexa(b & 15)); } String sha1 = sb.toString(); // Search artifact on maven.org URL searchUrl = new URL(MAVENORG_SEARCH_URL_PREFIX + sha1 + MAVENORG_SEARCH_URL_SUFFIX); boolean sourceAvailable = false; String id = null; String numFound = null; try (InputStream is = searchUrl.openStream()) { XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is); String name = ""; while (reader.hasNext()) { switch (reader.next()) { case XMLStreamConstants.START_ELEMENT: if ("str".equals(reader.getLocalName())) { if ("id".equals(reader.getAttributeValue(null, "name"))) { name = "id"; } else { name = "str"; } } else if ("result".equals(reader.getLocalName())) { numFound = reader.getAttributeValue(null, "numFound"); } else { name = ""; } break; case XMLStreamConstants.CHARACTERS: switch (name) { case "id": id = reader.getText().trim(); break; case "str": sourceAvailable |= "-sources.jar".equals(reader.getText().trim()); break; } break; } } reader.close(); } String groupId=null, artifactId=null, version=null; if ("0".equals(numFound)) { // File not indexed by Apache Solr of maven.org -> Try to found groupId, artifactId, version in 'pom.properties' Properties pomProperties = getPomProperties(entry); if (pomProperties != null) { groupId = pomProperties.getProperty("groupId"); artifactId = pomProperties.getProperty("artifactId"); version = pomProperties.getProperty("version"); } } else if ("1".equals(numFound) && sourceAvailable) { int index1 = id.indexOf(':'); int index2 = id.lastIndexOf(':'); groupId = id.substring(0, index1); artifactId = id.substring(index1+1, index2); version = id.substring(index2+1); } if (artifactId != null) { // Load source String filePath = groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version; URL loadUrl = new URL(MAVENORG_LOAD_URL_PREFIX + filePath + MAVENORG_LOAD_URL_SUFFIX); File tmpFile = File.createTempFile("jd-gui.tmp.", '.' + groupId + '_' + artifactId + '_' + version + "-sources.jar"); tmpFile.delete(); tmpFile.deleteOnExit(); try (InputStream is = new BufferedInputStream(loadUrl.openStream()); OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile))) { int read = is.read(buffer); while (read > 0) { os.write(buffer, 0, read); read = is.read(buffer); } } cache.put(entry, tmpFile); return tmpFile; } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } failed.add(entry); return null; } private static Properties getPomProperties(Container.Entry parent) { // Search 'META-INF/maven/*/*/pom.properties' for (Container.Entry child1 : parent.getChildren()) { if (child1.isDirectory() && child1.getPath().equals("META-INF")) { for (Container.Entry child2 : child1.getChildren()) { if (child2.isDirectory() && child2.getPath().equals("META-INF/maven")) { if (child2.isDirectory()) { Collection children = child2.getChildren(); if (children.size() == 1) { Container.Entry entry = children.iterator().next(); if (entry.isDirectory()) { children = entry.getChildren(); if (children.size() == 1) { entry = children.iterator().next(); for (Container.Entry child3 : entry.getChildren()) { if (!child3.isDirectory() && child3.getPath().endsWith("/pom.properties")) { // Load properties try (InputStream is = child3.getInputStream()) { Properties properties = new Properties(); properties.load(is); return properties; } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } } } } } } } } } } return null; } private static char hexa(int i) { return (char)( (i <= 9) ? ('0' + i) : (('a' - 10) + i) ); } protected boolean accepted(String filters, String path) { // 'filters' example : '+org +com.google +com.ibm +com.jcraft +com.springsource +com.sun -com +java +javax +sun +sunw' StringTokenizer tokenizer = new StringTokenizer(filters); while (tokenizer.hasMoreTokens()) { String filter = tokenizer.nextToken(); if (filter.length() > 1) { String prefix = filter.substring(1).replace('.', '/'); if (prefix.charAt(prefix.length() - 1) != '/') { prefix += '/'; } if (path.startsWith(prefix)) { return (filter.charAt(0) == '+'); } } } return false; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.spi.SourceSaver; import org.jd.gui.util.exception.ExceptionUtil; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; public abstract class AbstractSourceSaverProvider implements SourceSaver { protected List externalSelectors; protected Pattern externalPathPattern; /** * Initialize "selectors" and "pathPattern" with optional external properties file */ public AbstractSourceSaverProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".properties")) { if (is != null) { properties.load(is); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } init(properties); } protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); if (selectors != null) { externalSelectors = Arrays.asList(selectors.split(",")); } String pathRegExp = properties.getProperty("pathRegExp"); if (pathRegExp != null) { externalPathPattern = Pattern.compile(pathRegExp); } } protected String[] appendSelectors(String selector) { if (externalSelectors == null) { return new String[] { selector }; } else { int size = externalSelectors.size(); String[] array = new String[size+1]; externalSelectors.toArray(array); array[size] = selector; return array; } } protected String[] appendSelectors(String... selectors) { if (externalSelectors == null) { return selectors; } else { int size = externalSelectors.size(); String[] array = new String[size+selectors.length]; externalSelectors.toArray(array); System.arraycopy(selectors, 0, array, size, selectors.length); return array; } } public Pattern getPathPattern() { return externalPathPattern; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.util.decompiler.ContainerLoader; import org.jd.gui.util.decompiler.LineNumberStringBuilderPrinter; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.io.NewlineOutputStream; import java.io.*; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; public class ClassFileSourceSaverProvider extends AbstractSourceSaverProvider { protected static final String ESCAPE_UNICODE_CHARACTERS = "ClassFileDecompilerPreferences.escapeUnicodeCharacters"; protected static final String REALIGN_LINE_NUMBERS = "ClassFileDecompilerPreferences.realignLineNumbers"; protected static final String WRITE_LINE_NUMBERS = "ClassFileSaverPreferences.writeLineNumbers"; protected static final String WRITE_METADATA = "ClassFileSaverPreferences.writeMetadata"; protected static final String JD_CORE_VERSION = "JdGuiPreferences.jdCoreVersion"; protected static final ClassFileToJavaSourceDecompiler DECOMPILER = new ClassFileToJavaSourceDecompiler(); protected ContainerLoader loader = new ContainerLoader(); protected LineNumberStringBuilderPrinter printer = new LineNumberStringBuilderPrinter(); @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } @Override public String getSourcePath(Container.Entry entry) { String path = entry.getPath(); int index = path.lastIndexOf('.'); String prefix = (index == -1) ? path : path.substring(0, index); return prefix + ".java"; } @Override public int getFileCount(API api, Container.Entry entry) { if (entry.getPath().indexOf('$') == -1) { return 1; } else { return 0; } } @Override public void save(API api, Controller controller, Listener listener, Path rootPath, Container.Entry entry) { String sourcePath = getSourcePath(entry); Path path = rootPath.resolve(sourcePath); saveContent(api, controller, listener, rootPath, path, entry); } @Override public void saveContent(API api, Controller controller, Listener listener, Path rootPath, Path path, Container.Entry entry) { try { // Call listener if (path.toString().indexOf('$') == -1) { listener.pathSaved(path); } // Init preferences Map preferences = api.getPreferences(); boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, true); boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false); boolean showLineNumbers = getPreferenceValue(preferences, WRITE_LINE_NUMBERS, true); Map configuration = new HashMap<>(); configuration.put("realignLineNumbers", realignmentLineNumbers); // Init loader loader.setEntry(entry); // Init printer printer.setRealignmentLineNumber(realignmentLineNumbers); printer.setUnicodeEscape(unicodeEscape); printer.setShowLineNumbers(showLineNumbers); // Format internal name String entryPath = entry.getPath(); assert entryPath.endsWith(".class"); String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = ".class".length() // Decompile class file DECOMPILER.decompile(loader, printer, entryInternalName, configuration); StringBuilder stringBuffer = printer.getStringBuffer(); // Metadata if (getPreferenceValue(preferences, WRITE_METADATA, true)) { // Add location String location = new File(entry.getUri()).getPath() // Escape "\ u" sequence to prevent "Invalid unicode" errors .replaceAll("(^|[^\\\\])\\\\u", "\\\\\\\\u"); stringBuffer.append("\n\n/* Location: "); stringBuffer.append(location); // Add Java compiler version int majorVersion = printer.getMajorVersion(); if (majorVersion >= 45) { stringBuffer.append("\n * Java compiler version: "); if (majorVersion >= 49) { stringBuffer.append(majorVersion - (49 - 5)); } else { stringBuffer.append(majorVersion - (45 - 1)); } stringBuffer.append(" ("); stringBuffer.append(majorVersion); stringBuffer.append('.'); stringBuffer.append(printer.getMinorVersion()); stringBuffer.append(')'); } // Add JD-Core version stringBuffer.append("\n * JD-Core Version: "); stringBuffer.append(preferences.get(JD_CORE_VERSION)); stringBuffer.append("\n */"); } try (PrintStream ps = new PrintStream(new NewlineOutputStream(Files.newOutputStream(path)), true, "UTF-8")) { ps.print(stringBuffer.toString()); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); try (BufferedWriter writer = Files.newBufferedWriter(path, Charset.defaultCharset())) { writer.write("// INTERNAL ERROR //"); } catch (IOException ee) { assert ExceptionUtil.printStackTrace(ee); } } } protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { String v = preferences.get(key); return (v == null) ? defaultValue : Boolean.valueOf(v); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.spi.SourceSaver; import org.jd.gui.util.exception.ExceptionUtil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; public class DirectorySourceSaverProvider extends AbstractSourceSaverProvider { @Override public String[] getSelectors() { return appendSelectors("*:dir:*"); } @Override public String getSourcePath(Container.Entry entry) { return entry.getPath() + ".src.zip"; } @Override public int getFileCount(API api, Container.Entry entry) { return getFileCount(api, entry.getChildren()); } protected int getFileCount(API api, Collection entries) { int count = 0; for (Container.Entry e : entries) { SourceSaver sourceSaver = api.getSourceSaver(e); if (sourceSaver != null) { count += sourceSaver.getFileCount(api, e); } } return count; } @Override public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) { Path path = rootPath.resolve(entry.getPath()); try { Files.createDirectories(path); saveContent(api, controller, listener, rootPath, path, entry); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } @Override public void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) { for (Container.Entry e : getChildren(entry)) { if (controller.isCancelled()) { break; } SourceSaver sourceSaver = api.getSourceSaver(e); if (sourceSaver != null) { sourceSaver.save(api, controller, listener, rootPath, e); } } } protected Collection getChildren(Container.Entry entry) { return entry.getChildren(); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.spi.SourceSaver; import org.jd.gui.util.exception.ExceptionUtil; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; public class FileSourceSaverProvider extends AbstractSourceSaverProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*"); } @Override public String getSourcePath(Container.Entry entry) { return entry.getPath(); } @Override public int getFileCount(API api, Container.Entry entry) { return 1; } @Override public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) { saveContent(api, controller, listener, rootPath, rootPath.resolve(entry.getPath()), entry); } @Override public void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) { listener.pathSaved(path); try (InputStream is = entry.getInputStream()) { Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); try (BufferedWriter writer = Files.newBufferedWriter(path, Charset.defaultCharset())) { writer.write("// INTERNAL ERROR //"); } catch (IOException ee) { assert ExceptionUtil.printStackTrace(ee); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.api.model.Container; import org.jd.gui.util.container.JarContainerEntryUtil; import java.util.Collection; public class PackageSourceSaverProvider extends DirectorySourceSaverProvider { @Override public String[] getSelectors() { return appendSelectors("jar:dir:*", "war:dir:*", "ear:dir:*"); } @Override protected Collection getChildren(Container.Entry entry) { return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren()); } } ================================================ FILE: services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.sourcesaver; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.spi.SourceSaver; import org.jd.gui.util.exception.ExceptionUtil; import java.io.File; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; public class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*.zip", "*:file:*.jar", "*:file:*.war", "*:file:*.ear", "*:file:*.aar", "*:file:*.jmod", "*:file:*.kar"); } @Override public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) { try { String sourcePath = getSourcePath(entry); Path path = rootPath.resolve(sourcePath); Path parentPath = path.getParent(); if ((parentPath != null) && !Files.exists(parentPath)) { Files.createDirectories(parentPath); } File tmpSourceFile = api.loadSourceFile(entry); if (tmpSourceFile != null) { Files.copy(tmpSourceFile.toPath(), path); } else { File tmpFile = File.createTempFile("jd-gui.", ".tmp.zip"); tmpFile.delete(); tmpFile.deleteOnExit(); URI tmpFileUri = tmpFile.toURI(); URI tmpArchiveUri = new URI("jar:" + tmpFileUri.getScheme(), tmpFileUri.getHost(), tmpFileUri.getPath() + "!/", null); HashMap env = new HashMap<>(); env.put("create", "true"); FileSystem tmpArchiveFs = FileSystems.newFileSystem(tmpArchiveUri, env); Path tmpArchiveRootPath = tmpArchiveFs.getPath("/"); saveContent(api, controller, listener, tmpArchiveRootPath, tmpArchiveRootPath, entry); tmpArchiveFs.close(); Files.move(tmpFile.toPath(), path); } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.util.exception.ExceptionUtil; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.regex.Pattern; public abstract class AbstractTreeNodeFactoryProvider implements TreeNodeFactory { protected List externalSelectors; protected Pattern externalPathPattern; /** * Initialize "selectors" and "pathPattern" with optional external properties file */ public AbstractTreeNodeFactoryProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".properties")) { if (is != null) { properties.load(is); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } init(properties); } protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); if (selectors != null) { externalSelectors = Arrays.asList(selectors.split(",")); } String pathRegExp = properties.getProperty("pathRegExp"); if (pathRegExp != null) { externalPathPattern = Pattern.compile(pathRegExp); } } protected String[] appendSelectors(String selector) { if (externalSelectors == null) { return new String[] { selector }; } else { int size = externalSelectors.size(); String[] array = new String[size+1]; externalSelectors.toArray(array); array[size] = selector; return array; } } protected String[] appendSelectors(String... selectors) { if (externalSelectors == null) { return selectors; } else { int size = externalSelectors.size(); String[] array = new String[size+selectors.length]; externalSelectors.toArray(array); System.arraycopy(selectors, 0, array, size, selectors.length); return array; } } @Override public Pattern getPathPattern() { return externalPathPattern; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.TreeNodeExpandable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.spi.TypeFactory; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; public abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { protected static final TypeComparator TYPE_COMPARATOR = new TypeComparator(); protected static final FieldOrMethodBeanComparator FIELD_OR_METHOD_BEAN_COMPARATOR = new FieldOrMethodBeanComparator(); public static class BaseTreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, PageCreator { protected Container.Entry entry; protected PageAndTipFactory factory; protected URI uri; public BaseTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { super(userObject); this.entry = entry; this.factory = factory; if (fragment != null) { try { URI uri = entry.getUri(); this.uri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), fragment); } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } else { this.uri = entry.getUri(); } } // --- ContainerEntryGettable --- // @Override public Container.Entry getEntry() { return entry; } // --- UriGettable --- // @Override public URI getUri() { return uri; } // --- PageCreator --- // @Override public T createPage(API api) { // Lazy 'tip' initialization ((TreeNodeBean)userObject).setTip(factory.makeTip(api, entry)); return factory.makePage(api, entry); } } protected static class FileTreeNode extends BaseTreeNode implements TreeNodeExpandable { protected boolean initialized; public FileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) { this(entry, null, userObject, pageAndTipFactory); } public FileTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { super(entry, fragment, userObject, factory); initialized = false; // Add dummy node add(new DefaultMutableTreeNode()); } // --- TreeNodeExpandable --- // @Override public void populateTreeNode(API api) { if (!initialized) { removeAllChildren(); // Create type node TypeFactory typeFactory = api.getTypeFactory(entry); if (typeFactory != null) { Collection types = typeFactory.make(api, entry); for (Type type : types) { add(new TypeTreeNode(entry, type, new TreeNodeBean(type.getDisplayTypeName(), type.getIcon()), factory)); } } initialized = true; } } } protected static class TypeTreeNode extends BaseTreeNode implements TreeNodeExpandable { protected boolean initialized; protected Type type; public TypeTreeNode(Container.Entry entry, Type type, Object userObject, PageAndTipFactory factory) { super(entry, type.getName(), userObject, factory); this.initialized = false; this.type = type; // Add dummy node add(new DefaultMutableTreeNode()); } // --- TreeNodeExpandable --- // @Override public void populateTreeNode(API api) { if (!initialized) { removeAllChildren(); String typeName = type.getName(); // Create inner types Collection innerTypes = type.getInnerTypes(); if (innerTypes != null) { ArrayList innerTypeList = new ArrayList<>(innerTypes); innerTypeList.sort(TYPE_COMPARATOR); for (Type innerType : innerTypeList) { add(new TypeTreeNode(entry, innerType, new TreeNodeBean(innerType.getDisplayInnerTypeName(), innerType.getIcon()), factory)); } } // Create fields Collection fields = type.getFields(); if (fields != null) { ArrayList beans = new ArrayList<>(fields.size()); for (Type.Field field : fields) { String fragment = typeName + '-' + field.getName() + '-' + field.getDescriptor(); beans.add(new FieldOrMethodBean(fragment, field.getDisplayName(), field.getIcon())); } beans.sort(FIELD_OR_METHOD_BEAN_COMPARATOR); for (FieldOrMethodBean bean : beans) { add(new FieldOrMethodTreeNode(entry, bean.fragment, new TreeNodeBean(bean.label, bean.icon), factory)); } } // Create methods Collection methods = type.getMethods(); if (methods != null) { ArrayList beans = new ArrayList<>(); for (Type.Method method : methods) { if (!method.getName().equals("")) { String fragment = typeName + '-' + method.getName() + '-' + method.getDescriptor(); beans.add(new FieldOrMethodBean(fragment, method.getDisplayName(), method.getIcon())); } } beans.sort(FIELD_OR_METHOD_BEAN_COMPARATOR); for (FieldOrMethodBean bean : beans) { add(new FieldOrMethodTreeNode(entry, bean.fragment, new TreeNodeBean(bean.label, bean.icon), factory)); } } initialized = true; } } } protected static class FieldOrMethodTreeNode extends BaseTreeNode { public FieldOrMethodTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { super(entry, fragment, userObject, factory); } } protected static class FieldOrMethodBean { public String fragment, label; public Icon icon; public FieldOrMethodBean(String fragment, String label, Icon icon) { this.fragment = fragment; this.label = label; this.icon = icon; } } protected interface PageAndTipFactory { T makePage(API api, Container.Entry entry); String makeTip(API api, Container.Entry entry); } protected static class TypeComparator implements Comparator { @Override public int compare(Type type1, Type type2) { return type1.getName().compareTo(type2.getName()); } } protected static class FieldOrMethodBeanComparator implements Comparator { @Override public int compare(FieldOrMethodBean bean1, FieldOrMethodBean bean2) { return bean1.label.compareTo(bean2.label); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.component.DynamicPage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.regex.Pattern; public class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { protected static final ImageIcon CLASS_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/classf_obj.png")); protected static final Factory FACTORY = new Factory(); static { // Early class loading try { Class.forName(DynamicPage.class.getName()); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("^((?!module-info\\.class).)*$"); } else { return externalPathPattern; } } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf('/'); String label = entry.getPath().substring(lastSlashIndex+1); return (T)new FileTreeNode(entry, new TreeNodeBean(label, CLASS_FILE_ICON), FACTORY); } protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { @Override @SuppressWarnings("unchecked") public T makePage(API a, Container.Entry e) { return (T)new DynamicPage(a, e); } @Override public String makeTip(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); StringBuilder tip = new StringBuilder("Location: "); tip.append(location); tip.append("
Java compiler version: "); try (InputStream is = entry.getInputStream()) { is.skip(4); // Skip magic number int minorVersion = readUnsignedShort(is); int majorVersion = readUnsignedShort(is); if (majorVersion >= 49) { tip.append(majorVersion - (49-5)); } else if (majorVersion >= 45) { tip.append("1."); tip.append(majorVersion - (45-1)); } tip.append(" ("); tip.append(majorVersion); tip.append('.'); tip.append(minorVersion); tip.append(')'); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } tip.append(""); return tip.toString(); } /** * @see java.io.DataInputStream#readUnsignedShort() */ protected int readUnsignedShort(InputStream is) throws IOException { int ch1 = is.read(); int ch2 = is.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2 << 0); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ClassesDirectoryTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import javax.swing.*; public class ClassesDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ClassesDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/packagefolder_obj.png")); @Override public String[] getSelectors() { return appendSelectors( "jar:dir:META-INF/versions", "jar:dir:META-INF/versions/5", "jar:dir:META-INF/versions/6", "jar:dir:META-INF/versions/7", "jar:dir:META-INF/versions/8", "jar:dir:META-INF/versions/9", "jar:dir:META-INF/versions/10", "jar:dir:META-INF/versions/11", "jar:dir:META-INF/versions/12", "jar:dir:META-INF/versions/13", "jar:dir:META-INF/versions/14", "war:dir:WEB-INF/classes", "jmod:dir:classes"); } @Override public ImageIcon getIcon() { return ICON; } @Override public ImageIcon getOpenIcon() { return null; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class CssFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/css_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.css"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_CSS; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.TreeNodeExpandable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Container.Entry; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.net.URI; import java.util.Collection; public class DirectoryTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/folder.gif")); protected static final ImageIcon OPEN_ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/folder_open.png")); @Override public String[] getSelectors() { return appendSelectors("*:dir:*"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf('/'); Collection entries = entry.getChildren(); // Aggregate directory names while (entries.size() == 1) { Entry child = entries.iterator().next(); if (!child.isDirectory() || api.getTreeNodeFactory(child) != this || entry.getContainer() != child.getContainer()) break; entry = child; entries = entry.getChildren(); } String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); TreeNode node = new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, getIcon(), getOpenIcon())); if (entries.size() > 0) { // Add dummy node node.add(new DefaultMutableTreeNode()); } return (T)node; } public ImageIcon getIcon() { return ICON; } public ImageIcon getOpenIcon() { return OPEN_ICON; } protected static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, TreeNodeExpandable { Container.Entry entry; boolean initialized; public TreeNode(Container.Entry entry, Object userObject) { super(userObject); this.entry = entry; this.initialized = false; } // --- ContainerEntryGettable --- // @Override public Container.Entry getEntry() { return entry; } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } // --- TreeNodeExpandable --- // @Override public void populateTreeNode(API api) { if (!initialized) { removeAllChildren(); Collection entries = getChildren(); while (entries.size() == 1) { Entry child = entries.iterator().next(); if (!child.isDirectory() || api.getTreeNodeFactory(child) != this) { break; } entries = child.getChildren(); } for (Entry entry : entries) { TreeNodeFactory factory = api.getTreeNodeFactory(entry); if (factory != null) { add(factory.make(api, entry)); } } initialized = true; } } public Collection getChildren() { return entry.getChildren(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class DtdFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(DtdFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/dtd_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.dtd"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_DTD; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class EarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ear_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.ear"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.EjbJarXmlFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class EjbJarXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/xml_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("jar:file:META-INF/ejb-jar.xml"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean("ejb-jar.xml", "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new EjbJarXmlFilePage(api, entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.net.URI; public class FileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(FileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/file_plain_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf('/'); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable { protected Container.Entry entry; public TreeNode(Container.Entry entry, Object userObject) { super(userObject); this.entry = entry; } // --- ContainerEntryGettable --- // @Override public Container.Entry getEntry() { return entry; } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class HtmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/html_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.html", "*:file:*.xhtml"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_HTML; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ImageFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.api.model.Container; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.data.TreeNodeBean; import javax.imageio.ImageIO; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.awt.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; public class ImageFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ImageFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/file-image.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.gif", "*:file:*.jpg", "*:file:*.png"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new ImagePage(entry); } } protected static class ImagePage extends JPanel implements UriGettable { protected Container.Entry entry; public ImagePage(Container.Entry entry) { super(new BorderLayout()); this.entry = entry; try (InputStream is = entry.getInputStream()) { JScrollPane scrollPane = new JScrollPane(new JLabel(new ImageIcon(ImageIO.read(is)))); scrollPane.getHorizontalScrollBar().setUnitIncrement(16); scrollPane.getVerticalScrollBar().setUnitIncrement(16); add(scrollPane, BorderLayout.CENTER); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.util.container.JarContainerEntryUtil; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.util.Collection; public class JarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { protected static final ImageIcon JAR_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/jar_obj.png")); protected static final ImageIcon EJB_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ejbmodule_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.jar"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); ImageIcon icon = isAEjbModule(entry) ? EJB_FILE_ICON : JAR_FILE_ICON; T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, icon)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } protected static boolean isAEjbModule(Container.Entry entry) { Collection children = entry.getChildren(); if (children != null) { Container.Entry metaInf = null; for (Container.Entry child : children) { if (child.getPath().equals("META-INF")) { metaInf = child; break; } } if (metaInf != null) { children = metaInf.getChildren(); for (Container.Entry child : children) { if (child.getPath().equals("META-INF/ejb-jar.xml")) { return true; } } } } return false; } protected static class TreeNode extends ZipFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } @Override public Collection getChildren() { return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren()); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.JavaFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class JavaFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { protected static final ImageIcon JAVA_FILE_ICON = new ImageIcon(JavaFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/jcu_obj.png")); protected static final Factory FACTORY = new Factory(); @Override public String[] getSelectors() { return appendSelectors("*:file:*.java"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf('/'); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new FileTreeNode(entry, new TreeNodeBean(label, "Location: " + location, JAVA_FILE_ICON), FACTORY); } protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { // --- PageAndTipFactory --- // @Override @SuppressWarnings("unchecked") public T makePage(API a, Container.Entry e) { return (T)new JavaFilePage(a, e); } @Override public String makeTip(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); StringBuilder tip = new StringBuilder("Location: "); tip.append(location); tip.append(""); return tip.toString(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JavaModuleFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class JavaModuleFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*.jmod"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JavaModulePackageTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import java.util.regex.Pattern; public class JavaModulePackageTreeNodeFactoryProvider extends PackageTreeNodeFactoryProvider { @Override public String[] getSelectors() { return appendSelectors("jmod:dir:*"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("classes\\/(?!META-INF)..*"); } else { return externalPathPattern; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class JavascriptFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(JavascriptFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/js_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.js"); } @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class JsonFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(JsonFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ascii_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.json"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JSON; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class JspFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/html_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.jsp", "*:file:*.jspf"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JSP; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/KarFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class KarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*.kar"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.ManifestFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class ManifestFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/manifest_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:META-INF/MANIFEST.MF"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean("MANIFEST.MF", "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new ManifestFilePage(api, entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import javax.swing.*; import java.util.regex.Pattern; public class MetainfDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(MetainfDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/inf_obj.png")); @Override public String[] getSelectors() { return appendSelectors( "jar:dir:META-INF", "war:dir:WEB-INF", "war:dir:WEB-INF/classes/META-INF", "ear:dir:META-INF", "jmod:dir:classes/META-INF"); } @Override public ImageIcon getIcon() { return ICON; } @Override public ImageIcon getOpenIcon() { return null; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.OneTypeReferencePerLinePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.util.regex.Pattern; public class MetainfServiceFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ascii_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("META-INF\\/services\\/[^\\/]+"); } else { return externalPathPattern; } } @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new OneTypeReferencePerLinePage(api, entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ModuleInfoFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.spi.TypeFactory; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.view.component.ModuleInfoFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.util.Collection; import java.util.regex.Pattern; public class ModuleInfoFileTreeNodeFactoryProvider extends ClassFileTreeNodeFactoryProvider { protected static final ImageIcon MODULE_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/module_obj.png")); protected static final Factory FACTORY = new Factory(); static { // Early class loading try { Class.forName(ModuleInfoFilePage.class.getName()); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } @Override public String[] getSelectors() { return appendSelectors("*:file:*/module-info.class"); } @Override public Pattern getPathPattern() { return externalPathPattern; } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf('/'); String label = entry.getPath().substring(lastSlashIndex+1); return (T)new ModuleInfoFileTreeNode(entry, new TreeNodeBean(label, CLASS_FILE_ICON), FACTORY); } protected static class ModuleInfoFileTreeNode extends FileTreeNode { public ModuleInfoFileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) { super(entry, null, userObject, pageAndTipFactory); } // --- TreeNodeExpandable --- // @Override public void populateTreeNode(API api) { if (!initialized) { removeAllChildren(); // Create type node TypeFactory typeFactory = api.getTypeFactory(entry); if (typeFactory != null) { Collection types = typeFactory.make(api, entry); for (Type type : types) { add(new BaseTreeNode(entry, type.getName(), new TreeNodeBean(type.getDisplayTypeName(), MODULE_FILE_ICON), factory)); } } initialized = true; } } } protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { // --- PageAndTipFactory --- // @Override @SuppressWarnings("unchecked") public T makePage(API a, Container.Entry e) { return (T)new ModuleInfoFilePage(a, e); } @Override public String makeTip(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); StringBuilder tip = new StringBuilder("Location: "); tip.append(location); tip.append(""); return tip.toString(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.util.container.JarContainerEntryUtil; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.util.Collection; import java.util.regex.Pattern; public class PackageTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(PackageTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/package_obj.png")); @Override public String[] getSelectors() { return appendSelectors("jar:dir:*"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("(META-INF\\/versions\\/.*)|(?!META-INF)..*"); } else { return externalPathPattern; } } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); Collection entries = entry.getChildren(); // Aggregate directory names while (entries.size() == 1) { Container.Entry child = entries.iterator().next(); if (!child.isDirectory() || (api.getTreeNodeFactory(child) != this) || (entry.getContainer() != child.getContainer())) break; entry = child; entries = entry.getChildren(); } String label = entry.getPath().substring(lastSlashIndex+1).replace("/", "."); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, getIcon(), getOpenIcon())); if (entries.size() > 0) { // Add dummy node node.add(new DefaultMutableTreeNode()); } return node; } @Override public ImageIcon getIcon() { return ICON; } @Override public ImageIcon getOpenIcon() { return null; } protected static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } @Override public Collection getChildren() { return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren()); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class PropertiesFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(PropertiesFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ascii_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.properties"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/SpiFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import java.util.regex.Pattern; public class SpiFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { @Override public String[] getSelectors() { return appendSelectors("*:file:*"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("(.*\\/)?META-INF\\/services\\/.*"); } else { return externalPathPattern; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class SqlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(SqlFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/sql_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.sql"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_SQL; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rtextarea.Gutter; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.io.TextReader; import org.jd.gui.view.component.TextPage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; import java.io.IOException; import java.net.URI; public class TextFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/ascii_obj.png")); static { // Early class loading new Gutter(new RSyntaxTextArea()); try { Theme.load(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResourceAsStream("rsyntaxtextarea/themes/eclipse.xml")); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } @Override public String[] getSelectors() { return appendSelectors("*:file:*.txt", "*:file:*.md", "*:file:*.SF", "*:file:*.policy", "*:file:*.yaml", "*:file:*.yml", "*:file:*/COPYRIGHT", "*:file:*/LICENSE"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new Page(entry); } } protected static class Page extends TextPage implements UriGettable { protected Container.Entry entry; public Page(Container.Entry entry) { this.entry = entry; setText(TextReader.getText(entry.getInputStream())); } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf("/"); return path.substring(index+1); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class WarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/war_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.war"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import java.util.regex.Pattern; public class WarPackageTreeNodeFactoryProvider extends PackageTreeNodeFactoryProvider { @Override public String[] getSelectors() { return appendSelectors("war:dir:*"); } @Override public Pattern getPathPattern() { if (externalPathPattern == null) { return Pattern.compile("WEB-INF\\/classes\\/(?!META-INF)..*"); } else { return externalPathPattern; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.PageCreator; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.WebXmlFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class WebXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/xml_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("war:file:WEB-INF/web.xml"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean("web.xml", "Location: " + location, ICON)); } protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new WebXmlFilePage(api, entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import javax.swing.*; public class WebinfLibDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(WebinfLibDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/archivefolder_obj.png")); @Override public String[] getSelectors() { return appendSelectors("war:dir:WEB-INF/lib"); } @Override public ImageIcon getIcon() { return ICON; } @Override public ImageIcon getOpenIcon() { return null; } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class XmlBasedFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(XmlBasedFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/xml_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.xsl", "*:file:*.xslt", "*:file:*.xsd", "*:file:*.tld", "*:file:*.wsdl"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new TextFileTreeNodeFactoryProvider.Page(entry) { @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; } }; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.view.component.XmlFilePage; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class XmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(XmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/xml_obj.gif")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.xml"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); return (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); } protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- PageCreator --- // @Override @SuppressWarnings("unchecked") public T createPage(API api) { return (T)new XmlFilePage(api, entry); } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.treenode; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContainerEntryGettable; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.spi.TreeNodeFactory; import org.jd.gui.view.data.TreeNodeBean; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.io.File; public class ZipFileTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { protected static final ImageIcon ICON = new ImageIcon(ZipFileTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/zip_obj.png")); @Override public String[] getSelectors() { return appendSelectors("*:file:*.zip", "*:file:*.aar"); } @Override @SuppressWarnings("unchecked") public T make(API api, Container.Entry entry) { int lastSlashIndex = entry.getPath().lastIndexOf("/"); String label = entry.getPath().substring(lastSlashIndex+1); String location = new File(entry.getUri()).getPath(); T node = (T)new TreeNode(entry, new TreeNodeBean(label, "Location: " + location, ICON)); // Add dummy node node.add(new DefaultMutableTreeNode()); return node; } protected static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode { public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); } // --- TreeNodeExpandable --- // public void populateTreeNode(API api) { if (!initialized) { removeAllChildren(); for (Container.Entry e : getChildren()) { TreeNodeFactory factory = api.getTreeNodeFactory(e); if (factory != null) { add(factory.make(api, e)); } } initialized = true; } } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.type; import org.jd.gui.api.model.Type; import org.jd.gui.spi.TypeFactory; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.List; import java.util.regex.Pattern; public abstract class AbstractTypeFactoryProvider implements TypeFactory { protected List externalSelectors; protected Pattern externalPathPattern; /** * Initialize "selectors" and "pathPattern" with optional external properties file */ public AbstractTypeFactoryProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".properties")) { if (is != null) { properties.load(is); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } init(properties); } protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); externalSelectors = (selectors == null) ? null : Arrays.asList(selectors.split(",")); String pathRegExp = properties.getProperty("pathRegExp"); externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp); } protected String[] appendSelectors(String selector) { if (externalSelectors == null) { return new String[] { selector }; } else { int size = externalSelectors.size(); String[] array = new String[size+1]; externalSelectors.toArray(array); array[size] = selector; return array; } } protected String[] appendSelectors(String... selectors) { if (externalSelectors == null) { return selectors; } else { int size = externalSelectors.size(); String[] array = new String[size+selectors.length]; externalSelectors.toArray(array); System.arraycopy(selectors, 0, array, size, selectors.length); return array; } } public Pattern getPathPattern() { return externalPathPattern; } // Signature writers protected static int writeSignature(StringBuilder sb, String descriptor, int length, int index, boolean varargsFlag) { while (true) { // Print array : '[[?' ou '[L[?;' int dimensionLength = 0; if (descriptor.charAt(index) == '[') { dimensionLength++; while (++index < length) { if ((descriptor.charAt(index) == 'L') && (index+1 < length) && (descriptor.charAt(index+1) == '[')) { index++; length--; dimensionLength++; } else if (descriptor.charAt(index) == '[') { dimensionLength++; } else { break; } } } switch(descriptor.charAt(index)) { case 'B': sb.append("byte"); index++; break; case 'C': sb.append("char"); index++; break; case 'D': sb.append("double"); index++; break; case 'F': sb.append("float"); index++; break; case 'I': sb.append("int"); index++; break; case 'J': sb.append("long"); index++; break; case 'L': case '.': int beginIndex = ++index; char c = '.'; // Search ; or de < while (index < length) { c = descriptor.charAt(index); if ((c == ';') || (c == '<')) break; index++; } String internalClassName = descriptor.substring(beginIndex, index); int lastPackageSeparatorIndex = internalClassName.lastIndexOf('/'); if (lastPackageSeparatorIndex >= 0) { // Cut package name internalClassName = internalClassName.substring(lastPackageSeparatorIndex + 1); } sb.append(internalClassName.replace('$', '.')); if (c == '<') { sb.append('<'); index = writeSignature(sb, descriptor, length, index+1, false); while (descriptor.charAt(index) != '>') { sb.append(", "); index = writeSignature(sb, descriptor, length, index, false); } sb.append('>'); // pass '>' index++; } // pass ';' if (descriptor.charAt(index) == ';') index++; break; case 'S': sb.append("short"); index++; break; case 'T': beginIndex = ++index; index = descriptor.substring(beginIndex, length).indexOf(';'); sb.append(descriptor.substring(beginIndex, index)); index++; break; case 'V': sb.append("void"); index++; break; case 'Z': sb.append("boolean"); index++; break; case '-': sb.append("? super "); index = writeSignature(sb, descriptor, length, index+1, false); break; case '+': sb.append("? extends "); index = writeSignature(sb, descriptor, length, index+1, false); break; case '*': sb.append('?'); index++; break; case 'X': case 'Y': sb.append("int"); index++; break; default: throw new RuntimeException("SignatureWriter.WriteSignature: invalid signature '" + descriptor + "'"); } if (varargsFlag) { if (dimensionLength > 0) { while (--dimensionLength > 0) sb.append("[]"); sb.append("..."); } } else { while (dimensionLength-- > 0) sb.append("[]"); } if ((index >= length) || (descriptor.charAt(index) != '.')) break; sb.append('.'); } return index; } protected static void writeMethodSignature( StringBuilder sb, int typeAccess, int methodAccess, boolean isInnerClass, String constructorName, String methodName, String descriptor) { if (methodName.equals("")) { sb.append("{...}"); } else { boolean isAConstructor = methodName.equals(""); if (isAConstructor) { sb.append(constructorName); } else { sb.append(methodName); } // Skip generics int length = descriptor.length(); int index = 0; while ((index < length) && (descriptor.charAt(index) != '(')) index++; if (descriptor.charAt(index) != '(') { throw new RuntimeException("Signature format exception: '" + descriptor + "'"); } sb.append('('); // pass '(' index++; if (descriptor.charAt(index) != ')') { if (isAConstructor && isInnerClass && ((typeAccess & Type.FLAG_STATIC) == 0)) { // Skip first parameter int lengthBackup = sb.length(); index = writeSignature(sb, descriptor, length, index, false); sb.setLength(lengthBackup); } if (descriptor.charAt(index) != ')') { int varargsParameterIndex; if ((methodAccess & Type.FLAG_VARARGS) == 0) { varargsParameterIndex = Integer.MAX_VALUE; } else { // Count parameters int indexBackup = index; int lengthBackup = sb.length(); varargsParameterIndex = -1; while (descriptor.charAt(index) != ')') { index = writeSignature(sb, descriptor, length, index, false); varargsParameterIndex++; } index = indexBackup; sb.setLength(lengthBackup); } // Write parameters index = writeSignature(sb, descriptor, length, index, false); int parameterIndex = 1; while (descriptor.charAt(index) != ')') { sb.append(", "); index = writeSignature(sb, descriptor, length, index, (parameterIndex == varargsParameterIndex)); parameterIndex++; } } } if (isAConstructor) { sb.append(')'); } else { sb.append(") : "); writeSignature(sb, descriptor, length, ++index, false); } } } // Icon getters protected static ImageIcon getTypeIcon(int access) { if ((access & Type.FLAG_ANNOTATION) != 0) return ANNOTATION_ICON; else if ((access & Type.FLAG_INTERFACE) != 0) return INTERFACE_ICONS[accessToIndex(access)]; else if ((access & Type.FLAG_ENUM) != 0) return ENUM_ICON; else return CLASS_ICONS[accessToIndex(access)]; } protected static ImageIcon getFieldIcon(int access) { return FIELD_ICONS[accessToIndex(access)]; } protected static ImageIcon getMethodIcon(int access) { return METHOD_ICONS[accessToIndex(access)]; } protected static int accessToIndex(int access) { int index = 0; if ((access & Type.FLAG_STATIC) != 0) index += 4; if ((access & Type.FLAG_FINAL) != 0) index += 8; if ((access & Type.FLAG_ABSTRACT) != 0) index += 16; if ((access & Type.FLAG_PUBLIC) != 0) return index + 1; else if ((access & Type.FLAG_PROTECTED) != 0) return index + 2; else if ((access & Type.FLAG_PRIVATE) != 0) return index + 3; else return index; } // Internal graphic stuff ... protected static ImageIcon mergeIcons(ImageIcon background, ImageIcon overlay, int x, int y) { int w = background.getIconWidth(); int h = background.getIconHeight(); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); if (x + overlay.getIconWidth() > w) x = w - overlay.getIconWidth(); if (y + overlay.getIconHeight() > h) y = h - overlay.getIconHeight(); Graphics2D g2 = image.createGraphics(); g2.drawImage(background.getImage(), 0, 0, null); g2.drawImage(overlay.getImage(), x, y, null); g2.dispose(); return new ImageIcon(image); } protected static ImageIcon[] mergeIcons(ImageIcon[] backgrounds, ImageIcon overlay, int x, int y) { int length = backgrounds.length; ImageIcon[] result = new ImageIcon[length*2]; // Copy source icons System.arraycopy(backgrounds, 0, result, 0, length); // Add overlays for (int i=0; i extends LinkedHashMap { public static final int CACHE_MAX_ENTRIES = 100; public Cache() { super(CACHE_MAX_ENTRIES*3/2, 0.7f, true); } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > CACHE_MAX_ENTRIES; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.type; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.util.exception.ExceptionUtil; import org.objectweb.asm.*; import javax.swing.*; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { static { // Early class loading try { Class.forName(JavaType.class.getName()); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } // Create cache protected Cache cache = new Cache<>(); @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } @Override public Collection make(API api, Container.Entry entry) { return Collections.singletonList(make(api, entry, null)); } @Override public Type make(API api, Container.Entry entry, String fragment) { URI key = entry.getUri(); if (cache.containsKey(key)) { return cache.get(key); } else { JavaType type; try (InputStream is = entry.getInputStream()) { ClassReader classReader = new ClassReader(is); if ((fragment != null) && (fragment.length() > 0)) { // Search type name in fragment. URI format : see jd.gui.api.feature.UriOpener int index = fragment.indexOf('-'); if (index != -1) { // Keep type name only fragment = fragment.substring(0, index); } if (!classReader.getClassName().equals(fragment)) { // Search entry for type name String entryTypePath = classReader.getClassName() + ".class"; String fragmentTypePath = fragment + ".class"; while (true) { if (entry.getPath().endsWith(entryTypePath)) { // Entry path ends with the internal class name String pathToFound = entry.getPath().substring(0, entry.getPath().length() - entryTypePath.length()) + fragmentTypePath; Container.Entry entryFound = null; for (Container.Entry e : entry.getParent().getChildren()) { if (e.getPath().equals(pathToFound)) { entryFound = e; break; } } if (entryFound == null) return null; entry = entryFound; try (InputStream is2 = entry.getInputStream()) { classReader = new ClassReader(is2); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); return null; } break; } // Truncated path ? Cut first package name and retry int firstPackageSeparatorIndex = entryTypePath.indexOf('/'); if (firstPackageSeparatorIndex == -1) { // Nothing to cut -> Stop return null; } entryTypePath = entryTypePath.substring(firstPackageSeparatorIndex + 1); fragmentTypePath = fragmentTypePath.substring(fragmentTypePath.indexOf('/') + 1); } } } type = new JavaType(entry, classReader, -1); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); type = null; } cache.put(key, type); return type; } } static class JavaType implements Type { protected Container.Entry entry; protected int access; protected String name; protected String superName; protected String outerName; protected String displayTypeName; protected String displayInnerTypeName; protected String displayPackageName; protected List innerTypes; protected List fields = new ArrayList<>(); protected List methods = new ArrayList<>(); @SuppressWarnings("unchecked") protected JavaType(Container.Entry entry, ClassReader classReader, final int outerAccess) { this.entry = entry; ClassVisitor classAndInnerClassesVisitor = new ClassVisitor(Opcodes.ASM7) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { JavaType.this.access = (outerAccess == -1) ? access : outerAccess; JavaType.this.name = name; JavaType.this.superName = ((access & Opcodes.ACC_INTERFACE) != 0) && "java/lang/Object".equals(superName) ? null : superName; } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if (JavaType.this.name.equals(name)) { // Inner class path found JavaType.this.outerName = outerName; JavaType.this.displayInnerTypeName = innerName; } else if (((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_BRIDGE)) == 0) && JavaType.this.name.equals(outerName)) { Container.Entry innerEntry = getEntry(name); if (innerEntry != null) { try (InputStream is = innerEntry.getInputStream()) { ClassReader classReader = new ClassReader(is); if (innerTypes == null) { innerTypes = new ArrayList<>(); } innerTypes.add(new JavaType(innerEntry, classReader, access)); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } } }; classReader.accept(classAndInnerClassesVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); int lastPackageSeparatorIndex = name.lastIndexOf('/'); if (lastPackageSeparatorIndex == -1) { displayPackageName = ""; if (outerName == null) { displayTypeName = name; } else { displayTypeName = getDisplayTypeName(outerName, 0) + '.' + displayInnerTypeName; } } else { displayPackageName = name.substring(0, lastPackageSeparatorIndex).replace('/', '.'); if (outerName == null) { displayTypeName = name; } else { displayTypeName = getDisplayTypeName(outerName, lastPackageSeparatorIndex) + '.' + displayInnerTypeName; } displayTypeName = displayTypeName.substring(lastPackageSeparatorIndex+1); } ClassVisitor fieldsAndMethodsVisitor = new ClassVisitor(Opcodes.ASM7) { @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { if ((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM)) == 0) { fields.add(new Type.Field() { public int getFlags() { return access; } public String getName() { return name; } public String getDescriptor() { return descriptor; } public Icon getIcon() { return getFieldIcon(access); } public String getDisplayName() { StringBuilder sb = new StringBuilder(); sb.append(name).append(" : "); writeSignature(sb, descriptor, descriptor.length(), 0, false); return sb.toString(); } }); } return null; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if ((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM|Opcodes.ACC_BRIDGE)) == 0) { methods.add(new Type.Method() { public int getFlags() { return access; } public String getName() { return name; } public String getDescriptor() { return descriptor; } public Icon getIcon() { return getMethodIcon(access); } public String getDisplayName() { boolean isInnerClass = (JavaType.this.displayInnerTypeName != null); String constructorName = isInnerClass ? JavaType.this.displayInnerTypeName : JavaType.this.displayTypeName; StringBuilder sb = new StringBuilder(); writeMethodSignature(sb, JavaType.this.access, access, isInnerClass, constructorName, name, descriptor); return sb.toString(); } }); } return null; } }; classReader.accept(fieldsAndMethodsVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } @SuppressWarnings("unchecked") protected String getDisplayTypeName(String name, int packageLength) { int indexDollar = name.lastIndexOf('$'); if (indexDollar > packageLength) { Container.Entry entry = getEntry(name); if (entry != null) { try (InputStream is = entry.getInputStream()) { ClassReader classReader = new ClassReader(is); InnerClassVisitor classVisitor = new InnerClassVisitor(name); classReader.accept(classVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); String outerName = classVisitor.getOuterName(); if (outerName != null) { // Inner class path found => Recursive call return getDisplayTypeName(outerName, packageLength) + '.' + classVisitor.getInnerName(); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } return name; } protected Container.Entry getEntry(String typeName) { String pathToFound = typeName + ".class"; for (Container.Entry entry : entry.getParent().getChildren()) { if (entry.getPath().equals(pathToFound)) { return entry; } } return null; } @Override public int getFlags() { return access; } @Override public String getName() { return name; } @Override public String getSuperName() { return superName; } @Override public String getOuterName() { return outerName; } @Override public String getDisplayPackageName() { return displayPackageName; } @Override public String getDisplayTypeName() { return displayTypeName; } @Override public String getDisplayInnerTypeName() { return displayInnerTypeName; } @Override public Icon getIcon() { return getTypeIcon(access); } @Override public List getInnerTypes() { return innerTypes; } @Override public List getFields() { return fields; } @Override public List getMethods() { return methods; } } protected static class InnerClassVisitor extends ClassVisitor { protected String name; protected String outerName; protected String innerName; public InnerClassVisitor(String name) { super(Opcodes.ASM7); this.name = name; } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if (this.name.equals(name)) { // Inner class path found this.outerName = outerName; this.innerName = innerName; } } public String getOuterName() { return outerName; } public String getInnerName() { return innerName; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.type; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Type; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.parser.antlr.ANTLRJavaParser; import org.jd.gui.util.parser.antlr.AbstractJavaListener; import org.jd.gui.util.parser.antlr.JavaParser; import javax.swing.*; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.*; public class JavaFileTypeFactoryProvider extends AbstractTypeFactoryProvider { static { // Early class loading ANTLRJavaParser.parse(new ANTLRInputStream("class EarlyLoading{}"), new Listener(null)); } // Create cache protected Cache cache = new Cache<>(); @Override public String[] getSelectors() { return appendSelectors("*:file:*.java"); } @Override public Collection make(API api, Container.Entry entry) { Listener listener = getListener(entry); if (listener == null) { return Collections.emptyList(); } else { return listener.getRootTypes(); } } @Override public Type make(API api, Container.Entry entry, String fragment) { Listener listener = getListener(entry); if (listener == null) { return null; } else { if ((fragment != null) && (fragment.length() > 0)) { // Search type name in fragment. URI format : see jd.gui.api.feature.UriOpener int index = fragment.indexOf('-'); if (index != -1) { // Keep type name only fragment = fragment.substring(0, index); } return listener.getType(fragment); } else { return listener.getMainType(); } } } protected Listener getListener(Container.Entry entry) { URI key = entry.getUri(); if (cache.containsKey(key)) { return cache.get(key); } else { Listener listener; try (InputStream inputStream = entry.getInputStream()) { ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener = new Listener(entry)); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); listener = null; } cache.put(key, listener); return listener; } } protected static class JavaType implements Type { protected int access; protected String name; protected String superName; protected String outerName; protected String displayTypeName; protected String displayInnerTypeName; protected String displayPackageName; protected List innerTypes = new ArrayList<>(); protected List fields = new ArrayList<>(); protected List methods = new ArrayList<>(); protected JavaType outerType; public JavaType( int access, String name, String superName, String outerName, String displayTypeName, String displayInnerTypeName, String displayPackageName, JavaType outerType) { this.access = access; this.name = name; this.superName = superName; this.outerName = outerName; this.displayTypeName = displayTypeName; this.displayInnerTypeName = displayInnerTypeName; this.displayPackageName = displayPackageName; this.outerType = outerType; } public int getFlags() { return access; } public String getName() { return name; } public String getSuperName() { return superName; } public String getOuterName() { return outerName; } public String getDisplayTypeName() { return displayTypeName; } public String getDisplayInnerTypeName() { return displayInnerTypeName; } public String getDisplayPackageName() { return displayPackageName; } public Icon getIcon() { return getTypeIcon(access); } public JavaType getOuterType() { return outerType; } public Collection getInnerTypes() { return innerTypes; } public Collection getFields() { return fields; } public Collection getMethods() { return methods; } } protected static class JavaField implements Type.Field { protected int access; protected String name; protected String descriptor; public JavaField(int access, String name, String descriptor) { this.access = access; this.name = name; this.descriptor = descriptor; } public int getFlags() { return access; } public String getName() { return name; } public String getDescriptor() { return descriptor; } public Icon getIcon() { return getFieldIcon(access); } public String getDisplayName() { StringBuilder sb = new StringBuilder(); sb.append(name).append(" : "); writeSignature(sb, descriptor, descriptor.length(), 0, false); return sb.toString(); } } protected static class JavaMethod implements Type.Method { protected JavaType type; protected int access; protected String name; protected String descriptor; public JavaMethod(JavaType type, int access, String name, String descriptor) { this.type = type; this.access = access; this.name = name; this.descriptor = descriptor; } public int getFlags() { return access; } public String getName() { return name; } public String getDescriptor() { return descriptor; } public Icon getIcon() { return getMethodIcon(access); } public String getDisplayName() { String constructorName = type.getDisplayInnerTypeName(); boolean isInnerClass = (constructorName != null); if (constructorName == null) constructorName = type.getDisplayTypeName(); StringBuilder sb = new StringBuilder(); writeMethodSignature(sb, access, access, isInnerClass, constructorName, name, descriptor); return sb.toString(); } } protected static class Listener extends AbstractJavaListener { protected String displayPackageName = ""; protected JavaType mainType = null; protected JavaType currentType = null; protected ArrayList rootTypes = new ArrayList<>(); protected HashMap types = new HashMap<>(); public Listener(Container.Entry entry) { super(entry); } public Type getMainType() { return mainType; } public Type getType(String typeName) { return types.get(typeName); } public ArrayList getRootTypes() { return rootTypes; } // --- ANTLR Listener --- // public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { super.enterPackageDeclaration(ctx); displayPackageName = packageName.replace('/', '.'); } public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx, 0); } public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ENUM); } public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_INTERFACE); } public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ANNOTATION); } public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } protected void enterTypeDeclaration(ParserRuleContext ctx, int access) { String name = ctx.getToken(JavaParser.Identifier, 0).getText(); JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0); String superQualifiedTypeName; if (superType == null) { superQualifiedTypeName = ((access & JavaType.FLAG_INTERFACE) == 0) ? "java/lang/Object" : ""; } else { superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier()); } ParserRuleContext parent = ctx.getParent(); if (parent instanceof JavaParser.TypeDeclarationContext) access += getTypeDeclarationContextAccessFlag(parent); else if (parent instanceof JavaParser.MemberDeclarationContext) access += getMemberDeclarationContextAccessFlag(parent.getParent()); if (currentType == null) { String internalTypeName = packageName.isEmpty() ? name : packageName + "/" + name; String outerName = null; String displayTypeName = name; String displayInnerTypeName = null; currentType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, null); types.put(internalTypeName, currentType); rootTypes.add(currentType); nameToInternalTypeName.put(name, internalTypeName); if (mainType == null) { mainType = currentType; } else { // Multi class definitions in the same file String path = entry.getPath(); int index = path.lastIndexOf('/') + 1; if (path.substring(index).startsWith(name + '.')) { // Select the correct root type mainType = currentType; } } } else { String internalTypeName = currentType.getName() + '$' + name; String outerName = currentType.getName(); String displayTypeName = currentType.getDisplayTypeName() + '.' + name; String displayInnerTypeName = name; JavaType subType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, currentType); currentType.getInnerTypes().add(subType); currentType = subType; types.put(internalTypeName, currentType); nameToInternalTypeName.put(name, internalTypeName); } } protected void exitTypeDeclaration() { currentType = currentType.getOuterType(); } public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) { if (ctx.getChildCount() == 2) { ParseTree first = ctx.getChild(0); if (first instanceof TerminalNode) { if (((TerminalNode)first).getSymbol().getType() == JavaParser.STATIC) { currentType.getMethods().add(new JavaMethod(currentType, JavaType.FLAG_STATIC, "", "()V")); } } } } public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { JavaParser.TypeContext typeContext = ctx.type(); int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) { TerminalNode identifier = constantDeclaratorContext.Identifier(); String name = identifier.getText(); int dimensionOnVariable = countDimension(constantDeclaratorContext.children); String descriptor = createDescriptor(typeContext, dimensionOnVariable); currentType.getFields().add(new JavaField(access, name, descriptor)); } } public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { JavaParser.TypeContext typeContext = ctx.type(); int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { JavaParser.VariableDeclaratorIdContext variableDeclaratorId = declaration.variableDeclaratorId(); TerminalNode identifier = variableDeclaratorId.Identifier(); String name = identifier.getText(); int dimensionOnVariable = countDimension(variableDeclaratorId.children); String descriptor = createDescriptor(typeContext, dimensionOnVariable); currentType.getFields().add(new JavaField(access, name, descriptor)); } } public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); } public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); } public void enterMethodDeclaration( ParserRuleContext ctx, TerminalNode identifier, JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) { int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); String name = identifier.getText(); String paramDescriptors = createParamDescriptors(formalParameters.formalParameterList()); String returnDescriptor = createDescriptor(returnType, 0); String descriptor = paramDescriptors + returnDescriptor; currentType.getMethods().add(new JavaMethod(currentType, access, name, descriptor)); } public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent()); String paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList()); String descriptor = paramDescriptors + "V"; currentType.getMethods().add(new JavaMethod(currentType, access, "", descriptor)); } protected String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { StringBuilder paramDescriptors = null; if (formalParameterList != null) { List formalParameters = formalParameterList.formalParameter(); paramDescriptors = new StringBuilder("("); for (JavaParser.FormalParameterContext formalParameter : formalParameters) { int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children); paramDescriptors.append(createDescriptor(formalParameter.type(), dimensionOnParameter)); } } return (paramDescriptors == null) ? "()" : paramDescriptors.append(')').toString(); } protected int getTypeDeclarationContextAccessFlag(ParserRuleContext ctx) { int access = 0; for (JavaParser.ClassOrInterfaceModifierContext coiModifierContext : ctx.getRuleContexts(JavaParser.ClassOrInterfaceModifierContext.class)) { access += getAccessFlag(coiModifierContext); } return access; } protected int getMemberDeclarationContextAccessFlag(ParserRuleContext ctx) { int access = 0; for (JavaParser.ModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ModifierContext.class)) { JavaParser.ClassOrInterfaceModifierContext coiModifierContext = modifierContext.classOrInterfaceModifier(); if (coiModifierContext != null) { access += getAccessFlag(coiModifierContext); } } return access; } protected int getClassBodyDeclarationAccessFlag(ParserRuleContext ctx) { if ((currentType.access & JavaType.FLAG_INTERFACE) == 0) { int access = 0; for (JavaParser.ModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ModifierContext.class)) { JavaParser.ClassOrInterfaceModifierContext coimc = modifierContext.classOrInterfaceModifier(); if (coimc != null) { access += getAccessFlag(coimc); } } return access; } else { return JavaType.FLAG_PUBLIC; } } protected int getAccessFlag(JavaParser.ClassOrInterfaceModifierContext ctx) { if (ctx.getChildCount() == 1) { ParseTree first = ctx.getChild(0); if (first instanceof TerminalNode) { switch (((TerminalNode)first).getSymbol().getType()) { case JavaParser.STATIC: return JavaType.FLAG_STATIC; case JavaParser.FINAL: return JavaType.FLAG_FINAL; case JavaParser.ABSTRACT: return JavaType.FLAG_ABSTRACT; case JavaParser.PUBLIC: return JavaType.FLAG_PUBLIC; case JavaParser.PROTECTED: return JavaType.FLAG_PROTECTED; case JavaParser.PRIVATE: return JavaType.FLAG_PRIVATE; } } } return 0; } } } ================================================ FILE: services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.service.uriloader; import org.jd.gui.api.API; import org.jd.gui.spi.FileLoader; import org.jd.gui.spi.UriLoader; import java.io.File; import java.net.URI; public class FileUriLoaderProvider implements UriLoader { protected static final String[] SCHEMES = { "file" }; public String[] getSchemes() { return SCHEMES; } public boolean accept(API api, URI uri) { return "file".equals(uri.getScheme()); } public boolean load(API api, URI uri) { File file = new File(uri.getPath()); FileLoader fileLoader = api.getFileLoader(file); return (fileLoader != null) && fileLoader.load(api, file); } } ================================================ FILE: services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.container; import org.jd.gui.api.model.Container; import org.jd.gui.model.container.ContainerEntryComparator; import org.jd.gui.util.exception.ExceptionUtil; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; public class JarContainerEntryUtil { public static Collection removeInnerTypeEntries(Collection entries) { HashSet potentialOuterTypePaths = new HashSet<>(); Collection filtredSubEntries; for (Container.Entry e : entries) { if (!e.isDirectory()) { String p = e.getPath(); if (p.toLowerCase().endsWith(".class")) { int lastSeparatorIndex = p.lastIndexOf('/'); int dollarIndex = p.substring(lastSeparatorIndex+1).indexOf('$'); if (dollarIndex != -1) { potentialOuterTypePaths.add(p.substring(0, lastSeparatorIndex+1+dollarIndex) + ".class"); } } } } if (potentialOuterTypePaths.size() == 0) { filtredSubEntries = entries; } else { HashSet innerTypePaths = new HashSet<>(); for (Container.Entry e : entries) { if (!e.isDirectory() && potentialOuterTypePaths.contains(e.getPath())) { populateInnerTypePaths(innerTypePaths, e); } } filtredSubEntries = new ArrayList<>(); for (Container.Entry e : entries) { if (!e.isDirectory()) { String p = e.getPath(); if (p.toLowerCase().endsWith(".class")) { int indexDollar = p.lastIndexOf('$'); if (indexDollar != -1) { int indexSeparator = p.lastIndexOf('/'); if (indexDollar > indexSeparator) { if (innerTypePaths.contains(p)) { // Inner class found -> Skip continue; } else { populateInnerTypePaths(innerTypePaths, e); if (innerTypePaths.contains(p)) { // Inner class found -> Skip continue; } } } } } } // Valid path filtredSubEntries.add(e); } } List list = new ArrayList<>(filtredSubEntries); list.sort(ContainerEntryComparator.COMPARATOR); return list; } protected static void populateInnerTypePaths(final HashSet innerTypePaths, Container.Entry entry) { try (InputStream is = entry.getInputStream()) { ClassReader classReader = new ClassReader(is); String p = entry.getPath(); final String prefixPath = p.substring(0, p.length() - classReader.getClassName().length() - 6); ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM7) { public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) { innerTypePaths.add(prefixPath + name + ".class"); } }; classReader.accept(classVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/decompiler/ClassPathLoader.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.decompiler; import org.jd.core.v1.api.loader.Loader; import org.jd.core.v1.api.loader.LoaderException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class ClassPathLoader implements Loader { protected byte[] buffer = new byte[1024 * 4]; @Override public boolean canLoad(String internalName) { return this.getClass().getResource("/" + internalName + ".class") != null; } @Override public byte[] load(String internalName) throws LoaderException { InputStream is = this.getClass().getResourceAsStream("/" + internalName + ".class"); if (is == null) { return null; } else { try (InputStream input=is; ByteArrayOutputStream output=new ByteArrayOutputStream()) { int len = input.read(buffer); while (len > 0) { output.write(buffer, 0, len); len = input.read(buffer); } return output.toByteArray(); } catch (IOException e) { throw new LoaderException(e); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/decompiler/ContainerLoader.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.decompiler; import org.jd.core.v1.api.loader.Loader; import org.jd.core.v1.api.loader.LoaderException; import org.jd.gui.api.model.Container; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class ContainerLoader implements Loader { protected byte[] buffer = new byte[1024 * 4]; protected Container.Entry entry; public ContainerLoader() { this.entry = null; } public ContainerLoader(Container.Entry entry) { this.entry = entry; } public void setEntry(Container.Entry e) { this.entry = e; } protected Container.Entry getEntry(String internalPath) { String path = internalPath + ".class"; if (entry.getPath().equals(path)) { return entry; } for (Container.Entry e : entry.getParent().getChildren()) { if (e.getPath().equals(path)) { return e; } } return null; } // --- Loader --- // @Override public boolean canLoad(String internalName) { return getEntry(internalName) != null; } @Override public byte[] load(String internalName) throws LoaderException { Container.Entry entry = getEntry(internalName); if (entry == null) { return null; } else { try (InputStream input=entry.getInputStream(); ByteArrayOutputStream output=new ByteArrayOutputStream()) { int len = input.read(buffer); while (len > 0) { output.write(buffer, 0, len); len = input.read(buffer); } return output.toByteArray(); } catch (IOException e) { throw new LoaderException(e); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/decompiler/LineNumberStringBuilderPrinter.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.decompiler; public class LineNumberStringBuilderPrinter extends StringBuilderPrinter { protected boolean showLineNumbers = false; protected int maxLineNumber = 0; protected int digitCount = 0; protected String lineNumberBeginPrefix; protected String lineNumberEndPrefix; protected String unknownLineNumberPrefix; public void setShowLineNumbers(boolean showLineNumbers) { this.showLineNumbers = showLineNumbers; } protected int printDigit(int dcv, int lineNumber, int divisor, int left) { if (digitCount >= dcv) { if (lineNumber < divisor) { stringBuffer.append(' '); } else { int e = (lineNumber-left) / divisor; stringBuffer.append((char)('0' + e)); left += e*divisor; } } return left; } // --- Printer --- // @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) { super.start(maxLineNumber, majorVersion, minorVersion); if (showLineNumbers) { this.maxLineNumber = maxLineNumber; if (maxLineNumber > 0) { digitCount = 1; unknownLineNumberPrefix = " "; int maximum = 9; while (maximum < maxLineNumber) { digitCount++; unknownLineNumberPrefix += ' '; maximum = maximum*10 + 9; } lineNumberBeginPrefix = "/* "; lineNumberEndPrefix = " */ "; } else { unknownLineNumberPrefix = ""; lineNumberBeginPrefix = ""; lineNumberEndPrefix = ""; } } else { this.maxLineNumber = 0; unknownLineNumberPrefix = ""; lineNumberBeginPrefix = ""; lineNumberEndPrefix = ""; } } @Override public void startLine(int lineNumber) { if (maxLineNumber > 0) { stringBuffer.append(lineNumberBeginPrefix); if (lineNumber == UNKNOWN_LINE_NUMBER) { stringBuffer.append(unknownLineNumberPrefix); } else { int left = 0; left = printDigit(5, lineNumber, 10000, left); left = printDigit(4, lineNumber, 1000, left); left = printDigit(3, lineNumber, 100, left); left = printDigit(2, lineNumber, 10, left); stringBuffer.append((char)('0' + (lineNumber-left))); } stringBuffer.append(lineNumberEndPrefix); } for (int i=0; i 0) { if (maxLineNumber > 0) { stringBuffer.append(lineNumberBeginPrefix); stringBuffer.append(unknownLineNumberPrefix); stringBuffer.append(lineNumberEndPrefix); } stringBuffer.append(NEWLINE); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/decompiler/NopPrinter.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.decompiler; import org.jd.core.v1.api.printer.Printer; public class NopPrinter implements Printer { @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {} @Override public void end() {} @Override public void printText(String text) {} @Override public void printNumericConstant(String constant) {} @Override public void printStringConstant(String constant, String ownerInternalName) {} @Override public void printKeyword(String keyword) {} @Override public void printDeclaration(int flags, String internalTypeName, String name, String descriptor) {} @Override public void printReference(int flags, String internalTypeName, String name, String descriptor, String ownerInternalName) {} @Override public void indent() {} @Override public void unindent() {} @Override public void startLine(int lineNumber) {} @Override public void endLine() {} @Override public void extraLine(int count) {} @Override public void startMarker(int type) {} @Override public void endMarker(int type) {} } ================================================ FILE: services/src/main/java/org/jd/gui/util/decompiler/StringBuilderPrinter.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.decompiler; import org.jd.core.v1.api.printer.Printer; public class StringBuilderPrinter implements Printer { protected static final String TAB = " "; protected static final String NEWLINE = "\n"; protected StringBuilder stringBuffer = new StringBuilder(10*1024); protected boolean unicodeEscape = true; protected boolean realignmentLineNumber = false; protected int majorVersion = 0; protected int minorVersion = 0; protected int indentationCount; public void setUnicodeEscape(boolean unicodeEscape) { this.unicodeEscape = unicodeEscape; } public void setRealignmentLineNumber(boolean realignmentLineNumber) { this.realignmentLineNumber = realignmentLineNumber; } public int getMajorVersion() { return majorVersion; } public int getMinorVersion() { return minorVersion; } public StringBuilder getStringBuffer() { return stringBuffer; } protected void escape(String s) { if (unicodeEscape && (s != null)) { int length = s.length(); for (int i=0; i> 3))); stringBuffer.append((char) ('0' + (c & 0x7))); } else if (c > 127) { // Write octal format stringBuffer.append("\\u"); int z = (c >> 12); stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z))); z = ((c >> 8) & 0xF); stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z))); z = ((c >> 4) & 0xF); stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z))); z = (c & 0xF); stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z))); } else { stringBuffer.append(c); } } } else { stringBuffer.append(s); } } // --- Printer --- // @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) { this.stringBuffer.setLength(0); this.majorVersion = majorVersion; this.minorVersion = minorVersion; this.indentationCount = 0; } @Override public void end() {} @Override public void printText(String text) { escape(text); } @Override public void printNumericConstant(String constant) { escape(constant); } @Override public void printStringConstant(String constant, String ownerInternalName) { escape(constant); } @Override public void printKeyword(String keyword) { stringBuffer.append(keyword); } @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { escape(name); } @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { escape(name); } @Override public void indent() { indentationCount++; } @Override public void unindent() { if (indentationCount > 0) indentationCount--; } @Override public void startLine(int lineNumber) { for (int i=0; i 0) stringBuffer.append(NEWLINE); } @Override public void startMarker(int type) {} @Override public void endMarker(int type) {} } ================================================ FILE: services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.exception; public class ExceptionUtil { public static boolean printStackTrace(Throwable throwable) { throwable.printStackTrace(); return true; } } ================================================ FILE: services/src/main/java/org/jd/gui/util/index/IndexesUtil.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.index; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Future; public class IndexesUtil { public static boolean containsInternalTypeName(Collection> collectionOfFutureIndexes, String internalTypeName) { return contains(collectionOfFutureIndexes, "typeDeclarations", internalTypeName); } @SuppressWarnings("unchecked") public static List findInternalTypeName(Collection> collectionOfFutureIndexes, String internalTypeName) { return find(collectionOfFutureIndexes, "typeDeclarations", internalTypeName); } public static boolean contains(Collection> collectionOfFutureIndexes, String indexName, String key) { try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map index = futureIndexes.get().getIndex(indexName); if ((index != null) && (index.get(key) != null)) { return true; } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } return false; } @SuppressWarnings("unchecked") public static List find(Collection> collectionOfFutureIndexes, String indexName, String key) { ArrayList entries = new ArrayList<>(); try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map index = futureIndexes.get().getIndex(indexName); if (index != null) { Collection collection = index.get(key); if (collection != null) { entries.addAll(collection); } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } return entries; } } ================================================ FILE: services/src/main/java/org/jd/gui/util/io/NewlineOutputStream.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; public class NewlineOutputStream extends FilterOutputStream { private static byte[] lineSeparator; public NewlineOutputStream(OutputStream os) { super(os); if (lineSeparator == null) { String s = System.getProperty("line.separator"); if ((s == null) || (s.length() <= 0)) s = "\n"; lineSeparator = s.getBytes(Charset.forName("UTF-8")); } } public void write(int b) throws IOException { if (b == '\n') { out.write(lineSeparator); } else { out.write(b); } } public void write(byte b[]) throws IOException { write(b, 0, b.length); } public void write(byte b[], int off, int len) throws IOException { int i; for (i=off; i 0) { offset--; return true; } else { return false; } } public char get() { if (offset < length) return buffer[offset]; else return (char)0; } public boolean skipType() { if (offset < length) { char c = buffer[offset++]; while ((c == '[') && (offset < length)) { c = buffer[offset++]; } if (c == 'L') { while (offset < length) { if (buffer[offset++] == ';') return true; } } else if (c != '[') { return true; } } return false; } public boolean compareTypeWith(CharBuffer other) { if (offset >= length) return false; char c = buffer[offset++]; if (c != other.read()) return false; if (c == 'L') { if ((offset >= length) || (other.offset >= other.length)) return false; char[] otherBuffer = other.buffer; if ((buffer[offset] == '*') || (otherBuffer[other.offset] == '*')) { int start = offset; int otherStart = other.offset; // Search ';' if (!searchEndOfType() || !other.searchEndOfType()) return false; int current = offset - 1; int otherCurrent = other.offset - 1; // Backward comparison while ((start < current) && (otherStart < otherCurrent)) { c = buffer[--current]; if (c == '*') return true; char otherC = otherBuffer[--otherCurrent]; if (otherC == '*') return true; if (c != otherC) return false; } } else { // Forward comparison while (offset < length) { c = buffer[offset++]; if (c != other.read()) return false; if (c == ';') return true; } return false; } } return true; } protected boolean searchEndOfType() { while (offset < length) { if (buffer[offset++] == ';') return true; } return false; } public String toString() { return new String(buffer, offset, length-offset); } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.parser.antlr; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.jd.gui.util.exception.ExceptionUtil; public class ANTLRJavaParser { public static void parse(CharStream input, JavaListener listener) { try { JavaLexer lexer = new JavaLexer(input); lexer.removeErrorListeners(); CommonTokenStream tokens = new CommonTokenStream(lexer); JavaParser parser = new JavaParser(tokens); parser.removeErrorListeners(); ParseTree tree = parser.compilationUnit(); ParseTreeWalker.DEFAULT.walk(listener, tree); } catch (StackOverflowError e) { // Too complex source file, probably not written by a human. // This error may happen on Java file generated by ANTLR for example. assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.parser.antlr; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.v4.runtime.tree.TerminalNodeImpl; import org.jd.gui.api.model.Container; import org.jd.gui.util.exception.ExceptionUtil; import java.util.HashMap; import java.util.List; public abstract class AbstractJavaListener extends JavaBaseListener { protected Container.Entry entry; protected String packageName = ""; protected HashMap nameToInternalTypeName = new HashMap<>(); protected StringBuilder sb = new StringBuilder(); protected HashMap typeNameCache = new HashMap<>(); public AbstractJavaListener(Container.Entry entry) { this.entry = entry; } public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { packageName = concatIdentifiers(ctx.qualifiedName().Identifier()); } public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { List identifiers = ctx.qualifiedName().Identifier(); int size = identifiers.size(); if (size > 1) { nameToInternalTypeName.put(identifiers.get(size - 1).getText(), concatIdentifiers(identifiers)); } } protected String concatIdentifiers(List identifiers) { switch (identifiers.size()) { case 0: return ""; case 1: return identifiers.get(0).getText(); default: sb.setLength(0); for (TerminalNode identifier : identifiers) { sb.append(identifier.getText()).append('/'); } // Remove last separator sb.setLength(sb.length() - 1); return sb.toString(); } } protected String resolveInternalTypeName(List identifiers) { switch (identifiers.size()) { case 0: return null; case 1: // Search in cache String name = identifiers.get(0).getText(); String qualifiedName = typeNameCache.get(name); if (qualifiedName != null) { return qualifiedName; } // Search in imports String imp = nameToInternalTypeName.get(name); if (imp != null) { // Import found return imp; } // Search type in same package String prefix = name + '.'; if (entry.getPath().indexOf('/') != -1) { // Not in root package Container.Entry parent = entry.getParent(); int packageLength = parent.getPath().length() + 1; for (Container.Entry child : parent.getChildren()) { if (!child.isDirectory() && child.getPath().substring(packageLength).startsWith(prefix)) { qualifiedName = packageName + '/' + name; typeNameCache.put(name, qualifiedName); return qualifiedName; } } } // Search type in root package for (Container.Entry child : entry.getContainer().getRoot().getChildren()) { if (!child.isDirectory() && child.getPath().startsWith(prefix)) { typeNameCache.put(name, name); return name; } } // Search type in 'java.lang' try { if (Class.forName("java.lang." + name) != null) { qualifiedName = "java/lang/" + name; typeNameCache.put(name, qualifiedName); return qualifiedName; } } catch (ClassNotFoundException ignore) { // Ignore class loading error } // Type not found qualifiedName = "*/" + name; typeNameCache.put(name, qualifiedName); return qualifiedName; default: // Qualified type name -> Nothing to do return concatIdentifiers(identifiers); } } protected String createDescriptor(JavaParser.TypeContext typeContext, int dimension) { if (typeContext == null) { return "V"; } else { dimension += countDimension(typeContext.children); JavaParser.PrimitiveTypeContext primitive = typeContext.primitiveType(); String name; if (primitive == null) { JavaParser.ClassOrInterfaceTypeContext type = typeContext.classOrInterfaceType(); List typeArgumentsContexts = type.typeArguments(); if (typeArgumentsContexts.size() == 1) { JavaParser.TypeArgumentsContext typeArgumentsContext = typeArgumentsContexts.get(0); List typeArguments = typeArgumentsContext.typeArgument(); } else if (typeArgumentsContexts.size() > 1) { throw new RuntimeException("UNEXPECTED"); } name = "L" + resolveInternalTypeName(type.Identifier()) + ";"; } else { // Search primitive switch (primitive.getText()) { case "boolean": name = "Z"; break; case "byte": name = "B"; break; case "char": name = "C"; break; case "double": name = "D"; break; case "float": name = "F"; break; case "int": name = "I"; break; case "long": name = "J"; break; case "short": name = "S"; break; case "void": name = "V"; break; default: throw new RuntimeException("UNEXPECTED PRIMITIVE"); } } switch (dimension) { case 0: return name; case 1: return "[" + name; case 2: return "[[" + name; default: return new String(new char[dimension]).replace('\0', '[') + name; } } } protected int countDimension(List children) { int dimension = 0; for (ParseTree child : children) { if (child instanceof TerminalNodeImpl) { if (((TerminalNodeImpl)child).getSymbol().getType() == JavaParser.LBRACK) dimension++; } } return dimension; } } ================================================ FILE: services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.util.xml; import org.jd.gui.util.exception.ExceptionUtil; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.io.StringReader; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; public abstract class AbstractXmlPathFinder { protected HashMap> tagNameToPaths = new HashMap<>(); protected StringBuilder sb = new StringBuilder(200); public AbstractXmlPathFinder(Collection paths) { for (String path : paths) { if ((path != null) && (path.length() > 0)) { // Normalize path path = '/' + path; int lastIndex = path.lastIndexOf('/'); String lastTagName = path.substring(lastIndex+1); // Add tag names to map HashSet setOfPaths = tagNameToPaths.get(lastTagName); if (setOfPaths == null) { tagNameToPaths.put(lastTagName, setOfPaths = new HashSet<>()); } setOfPaths.add(path); } } } public void find(String text) { sb.setLength(0); try { XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(text)); String tagName = ""; int offset = 0; while (reader.hasNext()) { reader.next(); switch (reader.getEventType()) { case XMLStreamReader.START_ELEMENT: sb.append('/').append(tagName = reader.getLocalName()); offset = reader.getLocation().getCharacterOffset(); break; case XMLStreamReader.END_ELEMENT: sb.setLength(sb.length() - reader.getLocalName().length() - 1); break; case XMLStreamReader.CHARACTERS: HashSet setOfPaths = tagNameToPaths.get(tagName); if (setOfPaths != null) { String path = sb.toString(); if (setOfPaths.contains(path)) { // Search start offset while (offset > 0) { if (text.charAt(offset) == '>') { break; } else { offset--; } } handle(path.substring(1), reader.getText(), offset+1); } } break; } } } catch (XMLStreamException e) { assert ExceptionUtil.printStackTrace(e); } } public abstract void handle(String path, String text, int position); } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.*; import org.fife.ui.rsyntaxtextarea.folding.FoldManager; import org.fife.ui.rtextarea.*; import org.jd.gui.api.feature.ContentSearchable; import org.jd.gui.api.feature.LineNumberNavigable; import org.jd.gui.api.feature.PreferencesChangeListener; import org.jd.gui.api.feature.UriOpenable; import org.jd.gui.util.exception.ExceptionUtil; import javax.swing.*; import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelListener; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; public class AbstractTextPage extends JPanel implements LineNumberNavigable, ContentSearchable, UriOpenable, PreferencesChangeListener { protected static final String FONT_SIZE_KEY = "ViewerPreferences.fontSize"; protected static final ImageIcon COLLAPSED_ICON = new ImageIcon(AbstractTextPage.class.getClassLoader().getResource("org/jd/gui/images/plus.png")); protected static final ImageIcon EXPANDED_ICON = new ImageIcon(AbstractTextPage.class.getClassLoader().getResource("org/jd/gui/images/minus.png")); protected static final Color DOUBLE_CLICK_HIGHLIGHT_COLOR = new Color(0x66ff66); protected static final Color SEARCH_HIGHLIGHT_COLOR = new Color(0xffff66); protected static final Color SELECT_HIGHLIGHT_COLOR = new Color(0xF49810); protected static final RSyntaxTextAreaEditorKit.DecreaseFontSizeAction DECREASE_FONT_SIZE_ACTION = new RSyntaxTextAreaEditorKit.DecreaseFontSizeAction(); protected static final RSyntaxTextAreaEditorKit.IncreaseFontSizeAction INCREASE_FONT_SIZE_ACTION = new RSyntaxTextAreaEditorKit.IncreaseFontSizeAction(); protected RSyntaxTextArea textArea; protected RTextScrollPane scrollPane; protected Map preferences; public AbstractTextPage() { super(new BorderLayout()); textArea = newSyntaxTextArea(); textArea.setSyntaxEditingStyle(getSyntaxStyle()); textArea.setCodeFoldingEnabled(true); textArea.setAntiAliasingEnabled(true); textArea.setCaretPosition(0); textArea.setEditable(false); textArea.setDropTarget(null); textArea.setPopupMenu(null); textArea.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { textArea.setMarkAllHighlightColor(DOUBLE_CLICK_HIGHLIGHT_COLOR); SearchEngine.markAll(textArea, newSearchContext(textArea.getSelectedText(), true, true, true, false)); } } }); KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); InputMap inputMap = textArea.getInputMap(); inputMap.put(ctrlA, "none"); inputMap.put(ctrlC, "none"); inputMap.put(ctrlV, "none"); try { Theme theme = Theme.load(getClass().getClassLoader().getResourceAsStream("rsyntaxtextarea/themes/eclipse.xml")); theme.apply(textArea); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } scrollPane = new RTextScrollPane(textArea); scrollPane.setFoldIndicatorEnabled(true); scrollPane.setFont(textArea.getFont()); final MouseWheelListener[] mouseWheelListeners = scrollPane.getMouseWheelListeners(); // Remove default listeners for (MouseWheelListener listener : mouseWheelListeners) { scrollPane.removeMouseWheelListener(listener); } scrollPane.addMouseWheelListener(e -> { if ((e.getModifiers() & (Event.META_MASK|Event.CTRL_MASK)) != 0) { int x = e.getX() + scrollPane.getX() - textArea.getX(); int y = e.getY() + scrollPane.getY() - textArea.getY(); int offset = textArea.viewToModel(new Point(x, y)); // Update font size if (e.getWheelRotation() > 0) { DECREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea); } else { INCREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea); } // Save preferences if (preferences != null) { preferences.put(FONT_SIZE_KEY, String.valueOf(textArea.getFont().getSize())); } try { Rectangle newRectangle = textArea.modelToView(offset); int newY = newRectangle.y + (newRectangle.height >> 1); // Scroll Point viewPosition = scrollPane.getViewport().getViewPosition(); viewPosition.y = Math.max(viewPosition.y +newY - y, 0); scrollPane.getViewport().setViewPosition(viewPosition); } catch (BadLocationException ee) { assert ExceptionUtil.printStackTrace(ee); } } else { // Call default listeners for (MouseWheelListener listener : mouseWheelListeners) { listener.mouseWheelMoved(e); } } }); Gutter gutter = scrollPane.getGutter(); gutter.setFoldIcons(COLLAPSED_ICON, EXPANDED_ICON); gutter.setFoldIndicatorForeground(gutter.getBorderColor()); add(scrollPane, BorderLayout.CENTER); add(new RoundMarkErrorStrip(textArea), BorderLayout.LINE_END); } protected RSyntaxTextArea newSyntaxTextArea() { return new RSyntaxTextArea(); } public String getText() { return textArea.getText(); } public JScrollPane getScrollPane() { return scrollPane; } public void setText(String text) { textArea.setText(text); textArea.setCaretPosition(0); } public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_NONE; } /** * @see org.fife.ui.rsyntaxtextarea.RSyntaxUtilities#selectAndPossiblyCenter * Force center and do not select */ public void setCaretPositionAndCenter(DocumentRange range) { final int start = range.getStartOffset(); final int end = range.getEndOffset(); boolean foldsExpanded = false; FoldManager fm = textArea.getFoldManager(); if (fm.isCodeFoldingSupportedAndEnabled()) { foldsExpanded = fm.ensureOffsetNotInClosedFold(start); foldsExpanded |= fm.ensureOffsetNotInClosedFold(end); } if (!foldsExpanded) { try { Rectangle rec = textArea.modelToView(start); if (rec != null) { // Visible setCaretPositionAndCenter(start, end, rec); } else { // Not visible yet SwingUtilities.invokeLater(() -> { try { Rectangle r = textArea.modelToView(start); if (r != null) { setCaretPositionAndCenter(start, end, r); } } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); } }); } } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); } } } protected void setCaretPositionAndCenter(int start, int end, Rectangle r) { if (end != start) { try { r = r.union(textArea.modelToView(end)); } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); } } Rectangle visible = textArea.getVisibleRect(); // visible.x = r.x - (visible.width - r.width) / 2; visible.y = r.y - (visible.height - r.height) / 2; Rectangle bounds = textArea.getBounds(); Insets i = textArea.getInsets(); //bounds.x = i.left; bounds.y = i.top; //bounds.width -= i.left + i.right; bounds.height -= i.top + i.bottom; //if (visible.x < bounds.x) { // visible.x = bounds.x; //} //if (visible.x + visible.width > bounds.x + bounds.width) { // visible.x = bounds.x + bounds.width - visible.width; //} if (visible.y < bounds.y) { visible.y = bounds.y; } if (visible.y + visible.height > bounds.y + bounds.height) { visible.y = bounds.y + bounds.height - visible.height; } textArea.scrollRectToVisible(visible); textArea.setCaretPosition(start); } // --- LineNumberNavigable --- // public int getMaximumLineNumber() { try { return textArea.getLineOfOffset(textArea.getDocument().getLength()) + 1; } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); return 0; } } public void goToLineNumber(int lineNumber) { try { textArea.setCaretPosition(textArea.getLineStartOffset(lineNumber-1)); } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); } } public boolean checkLineNumber(int lineNumber) { return true; } // --- ContentSearchable --- // public boolean highlightText(String text, boolean caseSensitive) { if (text.length() > 1) { textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR); textArea.setCaretPosition(textArea.getSelectionStart()); SearchContext context = newSearchContext(text, caseSensitive, false, true, false); SearchResult result = SearchEngine.find(textArea, context); if (!result.wasFound()) { textArea.setCaretPosition(0); result = SearchEngine.find(textArea, context); } return result.wasFound(); } else { return true; } } public void findNext(String text, boolean caseSensitive) { if (text.length() > 1) { textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR); SearchContext context = newSearchContext(text, caseSensitive, false, true, false); SearchResult result = SearchEngine.find(textArea, context); if (!result.wasFound()) { textArea.setCaretPosition(0); SearchEngine.find(textArea, context); } } } public void findPrevious(String text, boolean caseSensitive) { if (text.length() > 1) { textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR); SearchContext context = newSearchContext(text, caseSensitive, false, false, false); SearchResult result = SearchEngine.find(textArea, context); if (!result.wasFound()) { textArea.setCaretPosition(textArea.getDocument().getLength()); SearchEngine.find(textArea, context); } } } protected SearchContext newSearchContext(String searchFor, boolean matchCase, boolean wholeWord, boolean searchForward, boolean regexp) { SearchContext context = new SearchContext(searchFor, matchCase); context.setMarkAll(true); context.setWholeWord(wholeWord); context.setSearchForward(searchForward); context.setRegularExpression(regexp); return context; } // --- UriOpenable --- // public boolean openUri(URI uri) { String query = uri.getQuery(); if (query != null) { Map parameters = parseQuery(query); if (parameters.containsKey("lineNumber")) { String lineNumber = parameters.get("lineNumber"); try { goToLineNumber(Integer.parseInt(lineNumber)); return true; } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else if (parameters.containsKey("position")) { String position = parameters.get("position"); try { int pos = Integer.parseInt(position); if (textArea.getDocument().getLength() > pos) { setCaretPositionAndCenter(new DocumentRange(pos, pos)); return true; } } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else if (parameters.containsKey("highlightFlags")) { String highlightFlags = parameters.get("highlightFlags"); if ((highlightFlags.indexOf('s') != -1) && parameters.containsKey("highlightPattern")) { textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR); textArea.setCaretPosition(0); // Highlight all String searchFor = createRegExp(parameters.get("highlightPattern")); SearchContext context = newSearchContext(searchFor, true, false, true, true); SearchResult result = SearchEngine.find(textArea, context); if (result.getMatchRange() != null) { textArea.setCaretPosition(result.getMatchRange().getStartOffset()); } return true; } } } return false; } protected Map parseQuery(String query) { HashMap parameters = new HashMap<>(); // Parse parameters try { for (String param : query.split("&")) { int index = param.indexOf('='); if (index == -1) { parameters.put(URLDecoder.decode(param, "UTF-8"), ""); } else { String key = param.substring(0, index); String value = param.substring(index + 1); parameters.put(URLDecoder.decode(key, "UTF-8"), URLDecoder.decode(value, "UTF-8")); } } } catch (UnsupportedEncodingException e) { assert ExceptionUtil.printStackTrace(e); } return parameters; } /** * Create a simple regular expression * * Rules: * '*' matchTypeEntries 0 ou N characters * '?' matchTypeEntries 1 character */ public static String createRegExp(String pattern) { int patternLength = pattern.length(); StringBuilder sbPattern = new StringBuilder(patternLength * 2); for (int i = 0; i < patternLength; i++) { char c = pattern.charAt(i); if (c == '*') { sbPattern.append(".*"); } else if (c == '?') { sbPattern.append('.'); } else if (c == '.') { sbPattern.append("\\."); } else { sbPattern.append(c); } } return sbPattern.toString(); } // --- PreferencesChangeListener --- // public void preferencesChanged(Map preferences) { String fontSize = preferences.get(FONT_SIZE_KEY); if (fontSize != null) { try { textArea.setFont(textArea.getFont().deriveFont(Float.parseFloat(fontSize))); } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } this.preferences = preferences; } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/ClassFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.core.v1.ClassFileToJavaSourceDecompiler; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.util.decompiler.*; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.io.NewlineOutputStream; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultCaret; import java.awt.*; import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; public class ClassFilePage extends TypePage { protected static final String ESCAPE_UNICODE_CHARACTERS = "ClassFileDecompilerPreferences.escapeUnicodeCharacters"; protected static final String REALIGN_LINE_NUMBERS = "ClassFileDecompilerPreferences.realignLineNumbers"; protected static final String WRITE_LINE_NUMBERS = "ClassFileSaverPreferences.writeLineNumbers"; protected static final String WRITE_METADATA = "ClassFileSaverPreferences.writeMetadata"; protected static final String JD_CORE_VERSION = "JdGuiPreferences.jdCoreVersion"; protected static final ClassFileToJavaSourceDecompiler DECOMPILER = new ClassFileToJavaSourceDecompiler(); protected int maximumLineNumber = -1; static { // Early class loading try { String internalTypeName = ClassFilePage.class.getName().replace('.', '/'); DECOMPILER.decompile(new ClassPathLoader(), new NopPrinter(), internalTypeName); } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); } } public ClassFilePage(API api, Container.Entry entry) { super(api, entry); Map preferences = api.getPreferences(); // Init view setErrorForeground(Color.decode(preferences.get("JdGuiPreferences.errorBackgroundColor"))); // Display source decompile(preferences); } public void decompile(Map preferences) { try { // Clear ... clearHyperlinks(); clearLineNumbers(); declarations.clear(); typeDeclarations.clear(); strings.clear(); // Init preferences boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false); boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false); Map configuration = new HashMap<>(); configuration.put("realignLineNumbers", realignmentLineNumbers); setShowMisalignment(realignmentLineNumbers); // Init loader ContainerLoader loader = new ContainerLoader(entry); // Init printer ClassFilePrinter printer = new ClassFilePrinter(); printer.setRealignmentLineNumber(realignmentLineNumbers); printer.setUnicodeEscape(unicodeEscape); // Format internal name String entryPath = entry.getPath(); assert entryPath.endsWith(".class"); String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = ".class".length() // Decompile class file DECOMPILER.decompile(loader, printer, entryInternalName, configuration); } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); setText("// INTERNAL ERROR //"); } maximumLineNumber = getMaximumSourceLineNumber(); } protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { String v = preferences.get(key); return (v == null) ? defaultValue : Boolean.valueOf(v); } @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; } // --- ContentSavable --- // @Override public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('.'); return path.substring(0, index) + ".java"; } @Override public void save(API api, OutputStream os) { try { // Init preferences Map preferences = api.getPreferences(); boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false); boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false); boolean showLineNumbers = getPreferenceValue(preferences, WRITE_LINE_NUMBERS, true); Map configuration = new HashMap<>(); configuration.put("realignLineNumbers", realignmentLineNumbers); // Init loader ContainerLoader loader = new ContainerLoader(entry); // Init printer LineNumberStringBuilderPrinter printer = new LineNumberStringBuilderPrinter(); printer.setRealignmentLineNumber(realignmentLineNumbers); printer.setUnicodeEscape(unicodeEscape); printer.setShowLineNumbers(showLineNumbers); // Format internal name String entryPath = entry.getPath(); assert entryPath.endsWith(".class"); String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = ".class".length() // Decompile class file DECOMPILER.decompile(loader, printer, entryInternalName, configuration); StringBuilder stringBuffer = printer.getStringBuffer(); // Metadata if (getPreferenceValue(preferences, WRITE_METADATA, true)) { // Add location String location = new File(entry.getUri()).getPath() // Escape "\ u" sequence to prevent "Invalid unicode" errors .replaceAll("(^|[^\\\\])\\\\u", "\\\\\\\\u"); stringBuffer.append("\n\n/* Location: "); stringBuffer.append(location); // Add Java compiler version int majorVersion = printer.getMajorVersion(); if (majorVersion >= 45) { stringBuffer.append("\n * Java compiler version: "); if (majorVersion >= 49) { stringBuffer.append(majorVersion - (49 - 5)); } else { stringBuffer.append(majorVersion - (45 - 1)); } stringBuffer.append(" ("); stringBuffer.append(majorVersion); stringBuffer.append('.'); stringBuffer.append(printer.getMinorVersion()); stringBuffer.append(')'); } // Add JD-Core version stringBuffer.append("\n * JD-Core Version: "); stringBuffer.append(preferences.get(JD_CORE_VERSION)); stringBuffer.append("\n */"); } try (PrintStream ps = new PrintStream(new NewlineOutputStream(os), true, "UTF-8")) { ps.print(stringBuffer.toString()); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); try (OutputStreamWriter writer = new OutputStreamWriter(os, Charset.defaultCharset())) { writer.write("// INTERNAL ERROR //"); } catch (IOException ee) { assert ExceptionUtil.printStackTrace(ee); } } } // --- LineNumberNavigable --- // @Override public int getMaximumLineNumber() { return maximumLineNumber; } @Override public void goToLineNumber(int lineNumber) { int textAreaLineNumber = getTextAreaLineNumber(lineNumber); if (textAreaLineNumber > 0) { try { int start = textArea.getLineStartOffset(textAreaLineNumber - 1); int end = textArea.getLineEndOffset(textAreaLineNumber - 1); setCaretPositionAndCenter(new DocumentRange(start, end)); } catch (BadLocationException e) { assert ExceptionUtil.printStackTrace(e); } } } @Override public boolean checkLineNumber(int lineNumber) { return lineNumber <= maximumLineNumber; } // --- PreferencesChangeListener --- // @Override public void preferencesChanged(Map preferences) { DefaultCaret caret = (DefaultCaret)textArea.getCaret(); int updatePolicy = caret.getUpdatePolicy(); caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); decompile(preferences); caret.setUpdatePolicy(updatePolicy); super.preferencesChanged(preferences); } public class ClassFilePrinter extends StringBuilderPrinter { protected HashMap referencesCache = new HashMap<>(); // Manage line number and misalignment int textAreaLineNumber = 1; @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) { super.start(maxLineNumber, majorVersion, minorVersion); if (maxLineNumber == 0) { scrollPane.setLineNumbersEnabled(false); } else { setMaxLineNumber(maxLineNumber); } } @Override public void end() { setText(stringBuffer.toString()); } // --- Add strings --- // @Override public void printStringConstant(String constant, String ownerInternalName) { if (constant == null) constant = "null"; if (ownerInternalName == null) ownerInternalName = "null"; strings.add(new TypePage.StringData(stringBuffer.length(), constant.length(), constant, ownerInternalName)); super.printStringConstant(constant, ownerInternalName); } @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { if (internalTypeName == null) internalTypeName = "null"; if (name == null) name = "null"; if (descriptor == null) descriptor = "null"; switch (type) { case TYPE: TypePage.DeclarationData data = new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, null, null); declarations.put(internalTypeName, data); typeDeclarations.put(stringBuffer.length(), data); break; case CONSTRUCTOR: declarations.put(internalTypeName + "--" + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, "", descriptor)); break; default: declarations.put(internalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, name, descriptor)); break; } super.printDeclaration(type, internalTypeName, name, descriptor); } @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { if (internalTypeName == null) internalTypeName = "null"; if (name == null) name = "null"; if (descriptor == null) descriptor = "null"; switch (type) { case TYPE: addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, null, null, ownerInternalName))); break; case CONSTRUCTOR: addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, "", descriptor, ownerInternalName))); break; default: addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, name, descriptor, ownerInternalName))); break; } super.printReference(type, internalTypeName, name, descriptor, ownerInternalName); } @Override public void startLine(int lineNumber) { super.startLine(lineNumber); setLineNumber(textAreaLineNumber, lineNumber); } @Override public void endLine() { super.endLine(); textAreaLineNumber++; } @Override public void extraLine(int count) { super.extraLine(count); if (realignmentLineNumber) { textAreaLineNumber += count; } } // --- Add references --- // public TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { String key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName; ReferenceData reference = referencesCache.get(key); if (reference == null) { reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName); referencesCache.put(key, reference); references.add(reference); } return reference; } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit; import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaUI; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; import org.fife.ui.rsyntaxtextarea.folding.Fold; import org.fife.ui.rsyntaxtextarea.folding.FoldManager; import org.fife.ui.rtextarea.Gutter; import org.fife.ui.rtextarea.LineNumberList; import org.fife.ui.rtextarea.RTextArea; import org.fife.ui.rtextarea.RTextAreaUI; import javax.swing.*; import javax.swing.text.EditorKit; import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.View; import java.awt.*; import java.util.Arrays; import java.util.Map; public abstract class CustomLineNumbersPage extends HyperlinkPage { protected Color errorForeground = Color.RED; protected boolean showMisalignment = true; public void setErrorForeground(Color color) { errorForeground = color; } public void setShowMisalignment(boolean b) { showMisalignment = b; } /** * Map[textarea line number] = original line number */ protected int[] lineNumberMap = null; protected int maxLineNumber = 0; protected void setMaxLineNumber(int maxLineNumber) { if (maxLineNumber > 0) { if (lineNumberMap == null) { lineNumberMap = new int[maxLineNumber+1]; } else if (lineNumberMap.length <= maxLineNumber) { int[] tmp = new int[maxLineNumber+1]; System.arraycopy(lineNumberMap, 0, tmp, 0, lineNumberMap.length); lineNumberMap = tmp; } this.maxLineNumber = maxLineNumber; } } protected void initLineNumbers() { String text = getText(); int len = text.length(); if (len == 0) { setMaxLineNumber(0); } else { int mln = len - text.replace("\n", "").length(); if (text.charAt(len-1) != '\n') { mln++; } setMaxLineNumber(mln); for (int i=1; i<=maxLineNumber; i++) { lineNumberMap[i] = i; } } } protected void setLineNumber(int textAreaLineNumber, int originalLineNumber) { if (originalLineNumber > 0) { setMaxLineNumber(textAreaLineNumber); lineNumberMap[textAreaLineNumber] = originalLineNumber; } } protected void clearLineNumbers() { if (lineNumberMap != null) { Arrays.fill(lineNumberMap, 0); } } protected int getMaximumSourceLineNumber() { return maxLineNumber; } protected int getTextAreaLineNumber(int originalLineNumber) { int textAreaLineNumber = 1; int greatestLowerSourceLineNumber = 0; int i = lineNumberMap.length; while (i-- > 0) { int sln = lineNumberMap[i]; if (sln <= originalLineNumber) { if (greatestLowerSourceLineNumber < sln) { greatestLowerSourceLineNumber = sln; textAreaLineNumber = i; } } } return textAreaLineNumber; } @Override protected RSyntaxTextArea newSyntaxTextArea() { return new SourceSyntaxTextArea(); } public class SourceSyntaxTextArea extends HyperlinkSyntaxTextArea { @Override protected RTextAreaUI createRTextAreaUI() { return new SourceSyntaxTextAreaUI(this); } } /** * A lot of code to replace the default LineNumberList... */ public class SourceSyntaxTextAreaUI extends RSyntaxTextAreaUI { public SourceSyntaxTextAreaUI(JComponent rSyntaxTextArea) { super(rSyntaxTextArea); } @Override public EditorKit getEditorKit(JTextComponent tc) { return new SourceSyntaxTextAreaEditorKit(); } @Override public Rectangle getVisibleEditorRect() { return super.getVisibleEditorRect(); } } public class SourceSyntaxTextAreaEditorKit extends RSyntaxTextAreaEditorKit { @Override public LineNumberList createLineNumberList(RTextArea textArea) { return new SourceLineNumberList(textArea); } } /** * Why 'LineNumberList' is so unexpandable ? Too many private fields & methods and too many package scope. */ public class SourceLineNumberList extends LineNumberList { protected RTextArea rTextArea; protected Map aaHints; protected Rectangle visibleRect; protected Insets textAreaInsets; protected Dimension preferredSize; public SourceLineNumberList(RTextArea textArea) { super(textArea, null); this.rTextArea = textArea; } @Override protected void init() { super.init(); visibleRect = new Rectangle(); aaHints = RSyntaxUtilities.getDesktopAntiAliasHints(); textAreaInsets = null; } /** * @see org.fife.ui.rtextarea.LineNumberList#paintComponent(java.awt.Graphics) */ @Override protected void paintComponent(Graphics g) { visibleRect = g.getClipBounds(visibleRect); if (visibleRect == null) { visibleRect = getVisibleRect(); } if (visibleRect == null) { return; } int cellWidth = getPreferredSize().width; int cellHeight = rTextArea.getLineHeight(); int ascent = rTextArea.getMaxAscent(); FoldManager fm = ((RSyntaxTextArea)rTextArea).getFoldManager(); int RHS_BORDER_WIDTH = getRhsBorderWidth(); FontMetrics metrics = g.getFontMetrics(); int rhs = getWidth() - RHS_BORDER_WIDTH; if (getParent() instanceof Gutter) { // Should always be true g.setColor(getParent().getBackground()); } else { g.setColor(getBackground()); } g.fillRect(0, visibleRect.y, cellWidth, visibleRect.height); g.setFont(getFont()); if (aaHints != null) { ((Graphics2D)g).addRenderingHints(aaHints); } if (rTextArea.getLineWrap()) { SourceSyntaxTextAreaUI ui = (SourceSyntaxTextAreaUI)rTextArea.getUI(); View v = ui.getRootView(rTextArea).getView(0); Element root = rTextArea.getDocument().getDefaultRootElement(); int lineCount = root.getElementCount(); int topPosition = rTextArea.viewToModel(visibleRect.getLocation()); int topLine = root.getElementIndex(topPosition); Rectangle visibleEditorRect = ui.getVisibleEditorRect(); Rectangle r = LineNumberList.getChildViewBounds(v, topLine, visibleEditorRect); int y = r.y; int visibleBottom = visibleRect.y + visibleRect.height; // Keep painting lines until our y-coordinate is past the visible // end of the text area. while (y < visibleBottom) { r = getChildViewBounds(v, topLine, visibleEditorRect); // Paint the line number. paintLineNumber(g, metrics, rhs, y+ascent, topLine + 1); // The next possible y-coordinate is just after the last line // painted. y += r.height; // Update topLine (we're actually using it for our "current line" // variable now). if (fm != null) { Fold fold = fm.getFoldForLine(topLine); if ((fold != null) && fold.isCollapsed()) { topLine += fold.getCollapsedLineCount(); } } if (++topLine >= lineCount) { break; } } } else { textAreaInsets = rTextArea.getInsets(textAreaInsets); if (visibleRect.y < textAreaInsets.top) { visibleRect.height -= (textAreaInsets.top - visibleRect.y); visibleRect.y = textAreaInsets.top; } int topLine = (visibleRect.y - textAreaInsets.top) / cellHeight; int actualTopY = topLine * cellHeight + textAreaInsets.top; int y = actualTopY + ascent; // Get the actual first line to paint, taking into account folding. topLine += fm.getHiddenLineCountAbove(topLine, true); // Paint line numbers g.setColor(getForeground()); int line = topLine + 1; while ((y < visibleRect.y + visibleRect.height + ascent) && (line <= rTextArea.getLineCount())) { paintLineNumber(g, metrics, rhs, y, line); y += cellHeight; if (fm != null) { Fold fold = fm.getFoldForLine(line - 1); // Skip to next line to paint, taking extra care for lines with // block ends and begins together, e.g. "} else {" while ((fold != null) && fold.isCollapsed()) { int hiddenLineCount = fold.getLineCount(); if (hiddenLineCount == 0) { // Fold parser identified a 0-line fold region... This // is really a bug, but we'll handle it gracefully. break; } line += hiddenLineCount; fold = fm.getFoldForLine(line - 1); } } line++; } } } protected void paintLineNumber(Graphics g, FontMetrics metrics, int x, int y, int lineNumber) { int originalLineNumber; if (lineNumberMap != null) { originalLineNumber = (lineNumber < lineNumberMap.length) ? lineNumberMap[lineNumber] : 0; } else { originalLineNumber = lineNumber; } if (originalLineNumber != 0) { String number = Integer.toString(originalLineNumber); int strWidth = metrics.stringWidth(number); g.setColor(showMisalignment && (lineNumber != originalLineNumber) ? errorForeground : getForeground()); g.drawString(number, x-strWidth, y); } } public int getRhsBorderWidth() { return ((RSyntaxTextArea)rTextArea).isCodeFoldingEnabled() ? 0 : 4; } @Override public Dimension getPreferredSize() { if (preferredSize == null) { int lineCount = getMaximumSourceLineNumber(); if (lineCount > 0) { Font font = getFont(); FontMetrics fontMetrics = getFontMetrics(font); int count = 1; while (lineCount >= 10) { lineCount = lineCount / 10; count++; } int preferredWidth = fontMetrics.charWidth('9') * count + 10; preferredSize = new Dimension(preferredWidth, 0); } else { preferredSize = new Dimension(0, 0); } } return preferredSize; } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/DynamicPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.jd.gui.api.API; import org.jd.gui.api.feature.*; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import javax.swing.*; import java.awt.*; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.Collection; import java.util.Map; import java.util.concurrent.Future; public class DynamicPage extends JPanel implements ContentCopyable, ContentSavable, ContentSearchable, ContentSelectable, FocusedTypeGettable, IndexesChangeListener, LineNumberNavigable, PreferencesChangeListener, UriGettable, UriOpenable, API.LoadSourceListener { protected API api; protected Container.Entry entry; protected TypePage page; protected URI lastOpenedUri; protected Collection> lastCollectionOfFutureIndexes; public DynamicPage(API api, Container.Entry entry) { super(new BorderLayout()); this.api = api; this.entry = entry; String source = api.getSource(entry); if (source == null) { // Display the decompiled source code add(page = new ClassFilePage(api, entry)); // Try to load source in background api.loadSource(entry, this); } else { // Display original source code add(page = new JavaFilePage(api, new DelegatedEntry(entry, source))); } } // --- ContentCopyable --- // @Override public void copy() { page.copy(); } // --- ContentSavable --- // @Override public String getFileName() { return page.getFileName(); } @Override public void save(API api, OutputStream outputStream) { page.save(api, outputStream); } // --- ContentSearchable --- // @Override public boolean highlightText(String text, boolean caseSensitive) { return page.highlightText(text, caseSensitive); } @Override public void findNext(String text, boolean caseSensitive) { page.findNext(text, caseSensitive); } @Override public void findPrevious(String text, boolean caseSensitive) { page.findPrevious(text, caseSensitive); } // --- ContentSelectable --- // @Override public void selectAll() { page.selectAll(); } // --- FocusedTypeGettable --- // @Override public String getFocusedTypeName() { return page.getFocusedTypeName(); } // --- ContainerEntryGettable --- // @Override public Container.Entry getEntry() { return entry; } // --- IndexesChangeListener --- // @Override public void indexesChanged(Collection> collectionOfFutureIndexes) { page.indexesChanged(lastCollectionOfFutureIndexes = collectionOfFutureIndexes); } // --- LineNumberNavigable --- // @Override public int getMaximumLineNumber() { return page.getMaximumLineNumber(); } @Override public void goToLineNumber(int lineNumber) { page.goToLineNumber(lineNumber); } @Override public boolean checkLineNumber(int lineNumber) { return page.checkLineNumber(lineNumber); } // --- PreferencesChangeListener --- // @Override public void preferencesChanged(Map preferences) { page.preferencesChanged(preferences); } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } // --- UriOpenable --- // @Override public boolean openUri(URI uri) { return page.openUri(lastOpenedUri = uri); } // --- LoadSourceListener --- // @Override public void sourceLoaded(String source) { SwingUtilities.invokeLater(() -> { // Replace the decompiled source code by the original Point viewPosition = page.getScrollPane().getViewport().getViewPosition(); removeAll(); add(page = new JavaFilePage(api, new DelegatedEntry(entry, source))); page.getScrollPane().getViewport().setViewPosition(viewPosition); if (lastOpenedUri != null) { page.openUri(lastOpenedUri); } if (lastCollectionOfFutureIndexes != null) { page.indexesChanged(lastCollectionOfFutureIndexes); } }); } protected static class DelegatedEntry implements Container.Entry { protected Container.Entry entry; protected String source; DelegatedEntry(Container.Entry entry, String source) { this.entry = entry; this.source = source; } @Override public Container getContainer() { return entry.getContainer(); } @Override public Container.Entry getParent() { return entry.getParent(); } @Override public URI getUri() { return entry.getUri(); } @Override public String getPath() { return entry.getPath(); } @Override public boolean isDirectory() { return entry.isDirectory(); } @Override public long length() { return entry.length(); } @Override public InputStream getInputStream() { return new ByteArrayInputStream(source.getBytes()); } @Override public Collection getChildren() { return entry.getChildren(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import org.jd.gui.util.io.TextReader; import org.jd.gui.util.xml.AbstractXmlPathFinder; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.List; import java.util.concurrent.Future; public class EjbJarXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { protected API api; protected Container.Entry entry; protected Collection> collectionOfFutureIndexes = Collections.emptyList(); public EjbJarXmlFilePage(API api, Container.Entry entry) { this.api = api; this.entry = entry; // Load content file String text = TextReader.getText(entry.getInputStream()); // Create hyperlinks new PathFinder().find(text); // Display setText(text); } public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData; if (data.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x - location.x, y - location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link String internalTypeName = data.internalTypeName; List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, data.internalTypeName); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, data.internalTypeName); } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (Map.Entry entry : hyperlinks.entrySet()) { TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue(); String internalTypeName = entryData.internalTypeName; boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); if (entryData.enabled != enabled) { entryData.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } public static final List typeHyperlinkPaths = Arrays.asList( "ejb-jar/assembly-descriptor/application-exception/exception-class", "ejb-jar/assembly-descriptor/interceptor-binding/interceptor-class", "ejb-jar/enterprise-beans/entity/home", "ejb-jar/enterprise-beans/entity/remote", "ejb-jar/enterprise-beans/entity/ejb-class", "ejb-jar/enterprise-beans/entity/prim-key-class", "ejb-jar/enterprise-beans/message-driven/ejb-class", "ejb-jar/enterprise-beans/message-driven/messaging-type", "ejb-jar/enterprise-beans/message-driven/resource-ref/injection-target/injection-target-class", "ejb-jar/enterprise-beans/message-driven/resource-env-ref/injection-target/injection-target-class", "ejb-jar/enterprise-beans/session/home", "ejb-jar/enterprise-beans/session/local", "ejb-jar/enterprise-beans/session/remote", "ejb-jar/enterprise-beans/session/business-local", "ejb-jar/enterprise-beans/session/business-remote", "ejb-jar/enterprise-beans/session/service-endpoint", "ejb-jar/enterprise-beans/session/ejb-class", "ejb-jar/enterprise-beans/session/ejb-ref/home", "ejb-jar/enterprise-beans/session/ejb-ref/remote", "ejb-jar/interceptors/interceptor/around-invoke/class", "ejb-jar/interceptors/interceptor/ejb-ref/home", "ejb-jar/interceptors/interceptor/ejb-ref/remote", "ejb-jar/interceptors/interceptor/interceptor-class" ); public class PathFinder extends AbstractXmlPathFinder { public PathFinder() { super(typeHyperlinkPaths); } public void handle(String path, String text, int position) { String trim = text.trim(); if (trim != null) { int startIndex = position + text.indexOf(trim); int endIndex = startIndex + trim.length(); String internalTypeName = trim.replace(".", "/"); addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName)); } } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.Token; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Map; import java.util.TreeMap; public abstract class HyperlinkPage extends TextPage { protected static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor(); protected static final Cursor HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); protected TreeMap hyperlinks = new TreeMap<>(); public HyperlinkPage() { MouseAdapter listener = new MouseAdapter() { int lastX = -1; int lastY = -1; int lastModifiers = -1; public void mouseClicked(MouseEvent e) { if ((e.getClickCount() == 1) && ((e.getModifiers() & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0)) { int offset = textArea.viewToModel(new Point(e.getX(), e.getY())); if (offset != -1) { Map.Entry entry = hyperlinks.floorEntry(offset); if (entry != null) { HyperlinkData entryData = entry.getValue(); if ((entryData != null) && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { openHyperlink(e.getXOnScreen(), e.getYOnScreen(), entryData); } } } } } public void mouseMoved(MouseEvent e) { if ((e.getX() != lastX) || (e.getY() != lastY) || (lastModifiers != e.getModifiers())) { lastX = e.getX(); lastY = e.getY(); lastModifiers = e.getModifiers(); if ((e.getModifiers() & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0) { int offset = textArea.viewToModel(new Point(e.getX(), e.getY())); if (offset != -1) { Map.Entry entry = hyperlinks.floorEntry(offset); if (entry != null) { HyperlinkData entryData = entry.getValue(); if ((entryData != null) && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { if (textArea.getCursor() != HAND_CURSOR) { textArea.setCursor(HAND_CURSOR); } return; } } } } if (textArea.getCursor() != DEFAULT_CURSOR) { textArea.setCursor(DEFAULT_CURSOR); } } } }; textArea.addMouseListener(listener); textArea.addMouseMotionListener(listener); } protected RSyntaxTextArea newSyntaxTextArea() { return new HyperlinkSyntaxTextArea(); } public void addHyperlink(HyperlinkData hyperlinkData) { hyperlinks.put(hyperlinkData.startPosition, hyperlinkData); } public void clearHyperlinks() { hyperlinks.clear(); } protected abstract boolean isHyperlinkEnabled(HyperlinkData hyperlinkData); protected abstract void openHyperlink(int x, int y, HyperlinkData hyperlinkData); public static class HyperlinkData { public int startPosition; public int endPosition; public HyperlinkData(int startPosition, int endPosition) { this.startPosition = startPosition; this.endPosition = endPosition; } } public class HyperlinkSyntaxTextArea extends RSyntaxTextArea { /** * @see HyperlinkPage.HyperlinkSyntaxTextArea#getUnderlineForToken(org.fife.ui.rsyntaxtextarea.Token) */ @Override public boolean getUnderlineForToken(Token t) { Map.Entry entry = hyperlinks.floorEntry(t.getOffset()); if (entry != null) { HyperlinkData entryData = entry.getValue(); if ((entryData != null) && (t.getOffset() < entryData.endPosition) && (t.getOffset() >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { return true; } } return super.getUnderlineForToken(t); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/JavaFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.util.io.TextReader; import org.jd.gui.util.parser.antlr.ANTLRJavaParser; import org.jd.gui.util.parser.antlr.AbstractJavaListener; import org.jd.gui.util.parser.antlr.JavaParser; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class JavaFilePage extends TypePage { public JavaFilePage(API api, Container.Entry entry) { super(api, entry); // Load content file String text = TextReader.getText(entry.getInputStream()).replace("\r\n", "\n").replace('\r', '\n'); // Parse DeclarationListener declarationListener = new DeclarationListener(entry); ReferenceListener referenceListener = new ReferenceListener(entry); ANTLRJavaParser.parse(new ANTLRInputStream(text), declarationListener); referenceListener.init(declarationListener); ANTLRJavaParser.parse(new ANTLRInputStream(text), referenceListener); // Display setText(text); initLineNumbers(); } public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } public class DeclarationListener extends AbstractJavaListener { protected StringBuilder sbTypeDeclaration = new StringBuilder(); protected String currentInternalTypeName; public DeclarationListener(Container.Entry entry) { super(entry); } public HashMap getNameToInternalTypeName() { return super.nameToInternalTypeName; } // --- Add declarations --- // public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { super.enterPackageDeclaration(ctx); if (! packageName.isEmpty()) { sbTypeDeclaration.append(packageName).append('/'); } } public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { List identifiers = ctx.qualifiedName().Identifier(); String internalTypeName = concatIdentifiers(identifiers); String typeName = identifiers.get(identifiers.size()-1).getSymbol().getText(); nameToInternalTypeName.put(typeName, internalTypeName); } public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } public void enterTypeDeclaration(ParserRuleContext ctx) { // Type declaration TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0); String typeName = identifier.getText(); int position = identifier.getSymbol().getStartIndex(); int length = sbTypeDeclaration.length(); if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) { sbTypeDeclaration.append(typeName); } else { sbTypeDeclaration.append('$').append(typeName); } currentInternalTypeName = sbTypeDeclaration.toString(); nameToInternalTypeName.put(typeName, currentInternalTypeName); // Super type reference JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0); String superInternalTypeName = (superType != null) ? resolveInternalTypeName(superType.classOrInterfaceType().Identifier()) : null; TypeDeclarationData data = new TypeDeclarationData(position, typeName.length(), currentInternalTypeName, null, null, superInternalTypeName); declarations.put(currentInternalTypeName, data); typeDeclarations.put(position, data); } public void exitTypeDeclaration() { int index = sbTypeDeclaration.lastIndexOf("$"); if (index == -1) { index = sbTypeDeclaration.lastIndexOf("/") + 1; } if (index == -1) { sbTypeDeclaration.setLength(0); } else { sbTypeDeclaration.setLength(index); } currentInternalTypeName = sbTypeDeclaration.toString(); } public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) { if (ctx.getChildCount() == 2) { ParseTree first = ctx.getChild(0); if (first instanceof TerminalNode) { TerminalNode f = (TerminalNode)first; if (f.getSymbol().getType() == JavaParser.STATIC) { String name = f.getText(); int position = f.getSymbol().getStartIndex(); declarations.put(currentInternalTypeName + "--()V", new TypePage.DeclarationData(position, 6, currentInternalTypeName, name, "()V")); } } } } public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { JavaParser.TypeContext typeContext = ctx.type(); for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) { TerminalNode identifier = constantDeclaratorContext.Identifier(); String name = identifier.getText(); int dimensionOnVariable = countDimension(constantDeclaratorContext.children); String descriptor = createDescriptor(typeContext, dimensionOnVariable); int position = identifier.getSymbol().getStartIndex(); declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)); } } public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { JavaParser.TypeContext typeContext = ctx.type(); for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { JavaParser.VariableDeclaratorIdContext variableDeclaratorId = declaration.variableDeclaratorId(); TerminalNode identifier = variableDeclaratorId.Identifier(); String name = identifier.getText(); int dimensionOnVariable = countDimension(variableDeclaratorId.children); String descriptor = createDescriptor(typeContext, dimensionOnVariable); int position = identifier.getSymbol().getStartIndex(); TypePage.DeclarationData data = new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor); declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, data); } } public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); } public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); } public void enterMethodDeclaration( ParserRuleContext ctx, TerminalNode identifier, JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) { String name = identifier.getText(); String paramDescriptors = createParamDescriptors(formalParameters.formalParameterList()); String returnDescriptor = createDescriptor(returnType, 0); String descriptor = paramDescriptors + returnDescriptor; int position = identifier.getSymbol().getStartIndex(); declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)); } public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { TerminalNode identifier = ctx.Identifier(); String name = identifier.getText(); String paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList()); String descriptor = paramDescriptors + "V"; int position = identifier.getSymbol().getStartIndex(); declarations.put(currentInternalTypeName + "--" + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)); } public String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { StringBuilder paramDescriptors = null; if (formalParameterList != null) { List formalParameters = formalParameterList.formalParameter(); paramDescriptors = new StringBuilder("("); for (JavaParser.FormalParameterContext formalParameter : formalParameters) { int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children); String descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter); paramDescriptors.append(descriptor); } } return (paramDescriptors == null) ? "()" : paramDescriptors.append(')').toString(); } } public class ReferenceListener extends AbstractJavaListener { protected StringBuilder sbTypeDeclaration = new StringBuilder(); protected HashMap referencesCache = new HashMap<>(); protected String currentInternalTypeName; protected Context currentContext = null; public ReferenceListener(Container.Entry entry) { super(entry); } public void init(DeclarationListener declarationListener) { this.nameToInternalTypeName.putAll(declarationListener.getNameToInternalTypeName()); } // --- Add declarations --- // public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { super.enterPackageDeclaration(ctx); if (! packageName.isEmpty()) { sbTypeDeclaration.append(packageName).append('/'); } } public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { List identifiers = ctx.qualifiedName().Identifier(); int position = identifiers.get(0).getSymbol().getStartIndex(); String internalTypeName = concatIdentifiers(identifiers); addHyperlink(new TypePage.HyperlinkReferenceData(position, internalTypeName.length(), newReferenceData(internalTypeName, null, null, null))); } public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } public void enterTypeDeclaration(ParserRuleContext ctx) { // Type declaration TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0); String typeName = identifier.getText(); int length = sbTypeDeclaration.length(); if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) { sbTypeDeclaration.append(typeName); } else { sbTypeDeclaration.append('$').append(typeName); } currentInternalTypeName = sbTypeDeclaration.toString(); currentContext = new Context(currentContext); } public void exitTypeDeclaration() { int index = sbTypeDeclaration.lastIndexOf("$"); if (index == -1) { index = sbTypeDeclaration.lastIndexOf("/") + 1; } if (index == -1) { sbTypeDeclaration.setLength(0); } else { sbTypeDeclaration.setLength(index); } currentInternalTypeName = sbTypeDeclaration.toString(); } public void enterFormalParameters(JavaParser.FormalParametersContext ctx) { JavaParser.FormalParameterListContext formalParameterList = ctx.formalParameterList(); if (formalParameterList != null) { List formalParameters = formalParameterList.formalParameter(); for (JavaParser.FormalParameterContext formalParameter : formalParameters) { int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children); String descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter); String name = formalParameter.variableDeclaratorId().Identifier().getSymbol().getText(); currentContext.nameToDescriptor.put(name, descriptor); } } } // --- Add references --- // public void enterType(JavaParser.TypeContext ctx) { // Add type reference JavaParser.ClassOrInterfaceTypeContext classOrInterfaceType = ctx.classOrInterfaceType(); if (classOrInterfaceType != null) { List identifiers = classOrInterfaceType.Identifier(); String name = concatIdentifiers(identifiers); String internalTypeName = resolveInternalTypeName(identifiers); int position = identifiers.get(0).getSymbol().getStartIndex(); addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))); } } public void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) { JavaParser.TypeContext typeContext = ctx.type(); for (JavaParser.VariableDeclaratorContext variableDeclarator : ctx.variableDeclarators().variableDeclarator()) { JavaParser.VariableDeclaratorIdContext variableDeclaratorId = variableDeclarator.variableDeclaratorId(); int dimensionOnVariable = countDimension(variableDeclaratorId.children); String descriptor = createDescriptor(typeContext, dimensionOnVariable); String name = variableDeclarator.variableDeclaratorId().Identifier().getSymbol().getText(); currentContext.nameToDescriptor.put(name, descriptor); } } public void enterCreator(JavaParser.CreatorContext ctx) { enterNewExpression(ctx.createdName().Identifier(), ctx.classCreatorRest()); } public void enterInnerCreator(JavaParser.InnerCreatorContext ctx) { enterNewExpression(Collections.singletonList(ctx.Identifier()), ctx.classCreatorRest()); } public void enterNewExpression(List identifiers, JavaParser.ClassCreatorRestContext classCreatorRest) { if (identifiers.size() > 0) { String name = concatIdentifiers(identifiers); String internalTypeName = resolveInternalTypeName(identifiers); int position = identifiers.get(0).getSymbol().getStartIndex(); if (classCreatorRest != null) { // Constructor call -> Add a link to the constructor declaration JavaParser.ExpressionListContext expressionList = classCreatorRest.arguments().expressionList(); String descriptor = (expressionList != null) ? getParametersDescriptor(expressionList).append('V').toString() : "()V"; addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, "", descriptor, currentInternalTypeName))); } else { // New type array -> Add a link to the type declaration addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))); } } } public void enterExpression(JavaParser.ExpressionContext ctx) { switch (ctx.getChildCount()) { case 1: TerminalNode identifier0 = getToken(ctx.children, JavaParser.Identifier, 0); if (identifier0 != null) { if (isAField(ctx)) { JavaParser.PrimaryContext primaryContext = ctx.primary(); if (primaryContext != null) { String fieldName = primaryContext.literal().StringLiteral().getText(); if (currentContext.getDescriptor(fieldName) == null) { // Not a local variable or a method parameter String fieldTypeName = searchInternalTypeNameForThisFieldName(currentInternalTypeName, fieldName); int position = ctx.Identifier().getSymbol().getStartIndex(); addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, "?", currentInternalTypeName))); } } } } else if (ctx.primary() != null) { TerminalNode identifier = ctx.primary().Identifier(); if (identifier != null) { Token symbol = identifier.getSymbol(); String name = symbol.getText(); String internalTypeName = nameToInternalTypeName.get(name); if (internalTypeName != null) { int position = symbol.getStartIndex(); addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))); } } } break; case 3: if (getToken(ctx.children, JavaParser.DOT, 1) != null) { // Search "expression '.' Identifier" : field reference TerminalNode identifier3 = getToken(ctx.children, JavaParser.Identifier, 2); if ((identifier3 != null) && isAField(ctx)) { String fieldTypeName = getInternalTypeName(ctx.getChild(0)); if (fieldTypeName != null) { int position = identifier3.getSymbol().getStartIndex(); String fieldName = identifier3.getText(); addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, "?", currentInternalTypeName))); } } } else if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) { // Search "expression '(' ')'" : method reference if (getToken(ctx.children, JavaParser.RPAREN, 2) != null) { enterCallMethodExpression(ctx, null); } } break; case 4: if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) { // Search "expression '(' expressionList ')'" : method reference if (getToken(ctx.children, JavaParser.RPAREN, 3) != null) { JavaParser.ExpressionListContext expressionListContext = ctx.expressionList(); if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) { enterCallMethodExpression(ctx, expressionListContext); } } } break; } } public void enterCallMethodExpression(JavaParser.ExpressionContext ctx, JavaParser.ExpressionListContext expressionListContext) { ParseTree first = ctx.children.get(0); if (first instanceof JavaParser.ExpressionContext) { JavaParser.ExpressionContext f = (JavaParser.ExpressionContext)first; switch (f.getChildCount()) { case 1: JavaParser.PrimaryContext primary = f.primary(); TerminalNode identifier = primary.Identifier(); if (identifier != null) { Token symbol = identifier.getSymbol(); if (symbol != null) { String methodName = symbol.getText(); String methodTypeName = searchInternalTypeNameForThisMethodName(currentInternalTypeName, methodName); if (methodTypeName != null) { int position = symbol.getStartIndex(); String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : "()?"; addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))); } } } else { Token symbol = primary.getChild(TerminalNode.class, 0).getSymbol(); if (symbol != null) { switch (symbol.getType()) { case JavaParser.THIS: int position = symbol.getStartIndex(); String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : "()?"; addHyperlink(new TypePage.HyperlinkReferenceData(position, 4, newReferenceData(currentInternalTypeName, "", methodDescriptor, currentInternalTypeName))); break; case JavaParser.SUPER: DeclarationData data = declarations.get(currentInternalTypeName); if (data instanceof TypeDeclarationData) { position = symbol.getStartIndex(); String methodTypeName = ((TypeDeclarationData) data).superTypeName; methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : "()?"; addHyperlink(new TypePage.HyperlinkReferenceData(position, 5, newReferenceData(methodTypeName, "", methodDescriptor, currentInternalTypeName))); } break; } } } break; case 3: // Search "expression '.' Identifier" ParseTree dot = first.getChild(1); if ((dot instanceof TerminalNode) && (((TerminalNode)dot).getSymbol().getType() == JavaParser.DOT)) { ParseTree identifier3 = first.getChild(2); if (identifier3 instanceof TerminalNode) { TerminalNode i3 = (TerminalNode)identifier3; if (i3.getSymbol().getType() == JavaParser.Identifier) { String methodTypeName = getInternalTypeName(first.getChild(0)); if (methodTypeName != null) { int position = i3.getSymbol().getStartIndex(); String methodName = i3.getText(); String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : "()?"; addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))); } } } } break; } } } public StringBuilder getParametersDescriptor(JavaParser.ExpressionListContext expressionListContext) { StringBuilder sb = new StringBuilder('('); for (JavaParser.ExpressionContext exp : expressionListContext.expression()) sb.append('?'); sb.append(')'); return sb; } public boolean isAField(JavaParser.ExpressionContext ctx) { RuleContext parent = ctx.parent; if (parent instanceof JavaParser.ExpressionContext) { int size = parent.getChildCount(); if (parent.getChild(size - 1) != ctx) { for (int i=0; i entry : declarations.entrySet()) { if (entry.getKey().startsWith(prefix) && (entry.getKey().charAt(length) != '(')) { return entry.getValue().typeName; } } // Not found int index = internalTypeName.lastIndexOf('$'); if (index != -1) { // Search in the outer type internalTypeName = internalTypeName.substring(0, index); return searchInternalTypeNameForThisFieldName(internalTypeName, name); } // Not found return null; } public String searchInternalTypeNameForThisMethodName(String internalTypeName, String name) { String prefix = internalTypeName + '-' + name + "-("; for (Map.Entry entry : declarations.entrySet()) { if (entry.getKey().startsWith(prefix)) { return entry.getValue().typeName; } } // Not found int index = internalTypeName.lastIndexOf('$'); if (index != -1) { // Search in the outer type internalTypeName = internalTypeName.substring(0, index); return searchInternalTypeNameForThisMethodName(internalTypeName, name); } // Not found return null; } public TerminalNode getToken(List children, int type, int i) { ParseTree pt = children.get(i); if (pt instanceof TerminalNode) { if (((TerminalNode)pt).getSymbol().getType() == type) { return (TerminalNode)pt; } } return null; } public void enterBlock(JavaParser.BlockContext ctx) { currentContext = new Context(currentContext); } public void exitBlock(JavaParser.BlockContext ctx) { currentContext = currentContext.outerContext; } public TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { String key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName; TypePage.ReferenceData reference = referencesCache.get(key); if (reference == null) { reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName); referencesCache.put(key, reference); references.add(reference); } return reference; } // --- Add strings --- // public void enterLiteral(JavaParser.LiteralContext ctx) { TerminalNode stringLiteral = ctx.StringLiteral(); if (stringLiteral != null) { String str = stringLiteral.getSymbol().getText(); int position = stringLiteral.getSymbol().getStartIndex(); strings.add(new TypePage.StringData(position, str.length(), str, currentInternalTypeName)); } } } public static class Context { protected Context outerContext; protected HashMap nameToDescriptor = new HashMap<>(); public Context(Context outerContext) { this.outerContext = outerContext; } /** * @param name Parameter or variable name * @return Qualified type name */ public String getDescriptor(String name) { String descriptor = nameToDescriptor.get(name); if ((descriptor == null) && (outerContext != null)) { descriptor = outerContext.getDescriptor(name); } return descriptor; } } public static class TypeDeclarationData extends TypePage.DeclarationData { protected String superTypeName; public TypeDeclarationData(int startPosition, int length, String type, String name, String descriptor, String superTypeName) { super(startPosition, length, type, name, descriptor); this.superTypeName = superTypeName; } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/LogPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import java.awt.*; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Future; public class LogPage extends HyperlinkPage implements UriGettable, IndexesChangeListener { protected API api; protected URI uri; protected Collection> collectionOfFutureIndexes = Collections.emptyList(); public LogPage(API api, URI uri, String content) { this.api = api; this.uri = uri; // Parse int index = 0; int eol = content.indexOf('\n'); while (eol != -1) { parseLine(content, index, eol); index = eol + 1; eol = content.indexOf('\n', index); } parseLine(content, index, content.length()); // Display setText(content); } protected void parseLine(String content, int index, int eol) { int start = content.indexOf("at ", index); if ((start != -1) && (start < eol)) { int leftParenthesisIndex = content.indexOf('(', start); if ((leftParenthesisIndex != -1) && (leftParenthesisIndex < eol)) { addHyperlink(new LogHyperlinkData(start+3, leftParenthesisIndex)); } } } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((LogHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { LogHyperlinkData logHyperlinkData = (LogHyperlinkData)hyperlinkData; if (logHyperlinkData.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x - location.x, y - location.y)); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link String text = getText(); String typeAndMethodNames = text.substring(hyperlinkData.startPosition, hyperlinkData.endPosition); int lastDotIndex = typeAndMethodNames.lastIndexOf('.'); String methodName = typeAndMethodNames.substring(lastDotIndex + 1); String internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/'); List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); int leftParenthesisIndex = hyperlinkData.endPosition + 1; int rightParenthesisIndex = text.indexOf(')', leftParenthesisIndex); String lineNumberOrNativeMethodFlag = text.substring(leftParenthesisIndex, rightParenthesisIndex); if (lineNumberOrNativeMethodFlag.equals("Native Method")) { // Example: at java.security.AccessController.doPrivileged(Native Method) lastDotIndex = internalTypeName.lastIndexOf('/'); String shortTypeName = internalTypeName.substring(lastDotIndex + 1); api.openURI(x, y, entries, null, shortTypeName + '-' + methodName + "-(*)?"); } else { // Example: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294) int colonIndex = lineNumberOrNativeMethodFlag.indexOf(':'); String lineNumber = lineNumberOrNativeMethodFlag.substring(colonIndex + 1); api.openURI(x, y, entries, "lineNumber=" + lineNumber, null); } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // public URI getUri() { return uri; } // --- ContentSavable --- // public String getFileName() { String path = uri.getPath(); int index = path.lastIndexOf('/'); return path.substring(index + 1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; String text = getText(); for (Map.Entry entry : hyperlinks.entrySet()) { LogHyperlinkData entryData = (LogHyperlinkData)entry.getValue(); String typeAndMethodNames = text.substring(entryData.startPosition, entryData.endPosition); int lastDotIndex = typeAndMethodNames.lastIndexOf('.'); String internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/'); boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); if (entryData.enabled != enabled) { entryData.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } public static class LogHyperlinkData extends HyperlinkData { public boolean enabled = false; public LogHyperlinkData(int startPosition, int endPosition) { super(startPosition, endPosition); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import org.jd.gui.util.io.TextReader; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.List; import java.util.concurrent.Future; public class ManifestFilePage extends HyperlinkPage implements UriGettable, IndexesChangeListener { protected API api; protected Container.Entry entry; protected Collection> collectionOfFutureIndexes = Collections.emptyList(); public ManifestFilePage(API api, Container.Entry entry) { this.api = api; this.entry = entry; // Load content file String text = TextReader.getText(entry.getInputStream()); // Parse hyperlinks. Docs: // - http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html // - http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html int startLineIndex = text.indexOf("Main-Class:"); if (startLineIndex != -1) { // Example: Main-Class: jd.gui.App int startIndex = skipSeparators(text, startLineIndex + "Main-Class:".length()); int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex); String typeName = text.substring(startIndex, endIndex); String internalTypeName = typeName.replace('.', '/'); addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + "-main-([Ljava/lang/String;)V")); } startLineIndex = text.indexOf("Premain-Class:"); if (startLineIndex != -1) { // Example: Premain-Class: packge.JavaAgent int startIndex = skipSeparators(text, startLineIndex + "Premain-Class:".length()); int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex); String typeName = text.substring(startIndex, endIndex); String internalTypeName = typeName.replace('.', '/'); // Undefined parameters : 2 candidate methods // http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + "-premain-(*)?")); } // Display setText(text); } public int skipSeparators(String text, int index) { int length = text.length(); while (index < length) { switch (text.charAt(index)) { case ' ': case '\t': case '\n': case '\r': index++; break; default: return index; } } return index; } public int searchEndIndexOfValue(String text, int startLineIndex, int startIndex) { int length = text.length(); int index = startIndex; while (index < length) { // MANIFEST.MF Specification: max line length = 72 // http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html switch (text.charAt(index)) { case '\r': // CR followed by LF ? if ((index-startLineIndex >= 70) && (index+1 < length) && (text.charAt(index+1) == ' ')) { // Multiline value startLineIndex = index+1; } else if ((index-startLineIndex >= 70) && (index+2 < length) && (text.charAt(index+1) == '\n') && (text.charAt(index+2) == ' ')) { // Multiline value index++; startLineIndex = index+1; } else { // (End of file) or (single line value) => return end index return index; } break; case '\n': if ((index-startLineIndex >= 70) && (index+1 < length) && (text.charAt(index+1) == ' ')) { // Multiline value startLineIndex = index+1; } else { // (End of file) or (single line value) => return end index return index; } break; } index++; } return index; } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((ManifestHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { ManifestHyperlinkData data = (ManifestHyperlinkData)hyperlinkData; if (data.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x-location.x, y-location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link String text = getText(); String textLink = getValue(text, hyperlinkData.startPosition, hyperlinkData.endPosition); String internalTypeName = textLink.replace('.', '/'); List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, data.fragment); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, data.fragment); } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; String text = getText(); for (Map.Entry entry : hyperlinks.entrySet()) { ManifestHyperlinkData entryData = (ManifestHyperlinkData)entry.getValue(); String textLink = getValue(text, entryData.startPosition, entryData.endPosition); String internalTypeName = textLink.replace('.', '/'); boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); if (entryData.enabled != enabled) { entryData.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } public static String getValue(String text, int startPosition, int endPosition) { return text // Extract text of link .substring(startPosition, endPosition) // Convert multiline value .replace("\r\n ", "") .replace("\r ", "") .replace("\n ", ""); } public static class ManifestHyperlinkData extends HyperlinkData { public boolean enabled; public String fragment; ManifestHyperlinkData(int startPosition, int endPosition, String fragment) { super(startPosition, endPosition); this.enabled = false; this.fragment = fragment; } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/ModuleInfoFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.*; import org.fife.ui.rtextarea.Marker; import org.jd.gui.api.API; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.decompiler.ContainerLoader; import org.jd.gui.util.decompiler.StringBuilderPrinter; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import javax.swing.text.Segment; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.List; import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.jd.core.v1.api.printer.Printer.MODULE; import static org.jd.core.v1.api.printer.Printer.PACKAGE; import static org.jd.core.v1.api.printer.Printer.TYPE; public class ModuleInfoFilePage extends ClassFilePage { public static final String SYNTAX_STYLE_JAVA_MODULE = "text/java-module"; static { // Add a new token maker for Java 9+ module AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance(); atmf.putMapping(SYNTAX_STYLE_JAVA_MODULE, ModuleInfoTokenMaker.class.getName()); } public ModuleInfoFilePage(API api, Container.Entry entry) { super(api, entry); } @Override public void decompile(Map preferences) { try { // Clear ... clearHyperlinks(); clearLineNumbers(); typeDeclarations.clear(); // Init preferences boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false); // Init loader ContainerLoader loader = new ContainerLoader(entry); // Init printer ModuleInfoFilePrinter printer = new ModuleInfoFilePrinter(); printer.setUnicodeEscape(unicodeEscape); // Format internal name String entryPath = entry.getPath(); assert entryPath.endsWith(".class"); String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = ".class".length() // Decompile class file DECOMPILER.decompile(loader, printer, entryInternalName); } catch (Throwable t) { assert ExceptionUtil.printStackTrace(t); setText("// INTERNAL ERROR //"); } } @Override public String getSyntaxStyle() { return SYNTAX_STYLE_JAVA_MODULE; } @Override protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { HyperlinkReferenceData hyperlinkReferenceData = (HyperlinkReferenceData)hyperlinkData; if (hyperlinkReferenceData.reference.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x - location.x, y - location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)hyperlinkReferenceData.reference; List entries; String fragment; switch (moduleInfoReferenceData.type) { case TYPE: entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, fragment = moduleInfoReferenceData.typeName); break; case PACKAGE: entries = IndexesUtil.find(collectionOfFutureIndexes, "packageDeclarations", moduleInfoReferenceData.typeName); fragment = null; break; default: // MODULE entries = IndexesUtil.find(collectionOfFutureIndexes, "javaModuleDeclarations", moduleInfoReferenceData.name); fragment = moduleInfoReferenceData.typeName; break; } if (entries.contains(entry)) { api.openURI(uri); } else { String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, fragment); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, fragment); } } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriOpenable --- // @Override public boolean openUri(URI uri) { ArrayList ranges = new ArrayList<>(); String fragment = uri.getFragment(); String query = uri.getQuery(); Marker.clearMarkAllHighlights(textArea); if ((fragment != null) && (declarations.size() == 1)) { DeclarationData declaration = declarations.entrySet().iterator().next().getValue(); if (fragment.equals(declaration.typeName)) { ranges.add(new DocumentRange(declaration.startPosition, declaration.endPosition)); } } if (query != null) { Map parameters = parseQuery(query); String highlightFlags = parameters.get("highlightFlags"); String highlightPattern = parameters.get("highlightPattern"); if ((highlightFlags != null) && (highlightPattern != null)) { String regexp = createRegExp(highlightPattern); Pattern pattern = Pattern.compile(regexp + ".*"); boolean t = (highlightFlags.indexOf('t') != -1); // Highlight types boolean M = (highlightFlags.indexOf('M') != -1); // Highlight modules if (highlightFlags.indexOf('d') != -1) { // Highlight declarations for (Map.Entry entry : declarations.entrySet()) { DeclarationData declaration = entry.getValue(); if (M) { matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges); } } } if (highlightFlags.indexOf('r') != -1) { // Highlight references for (Map.Entry entry : hyperlinks.entrySet()) { HyperlinkData hyperlink = entry.getValue(); ReferenceData reference = ((HyperlinkReferenceData)hyperlink).reference; ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)reference; if (t && (moduleInfoReferenceData.type == TYPE)) { matchAndAddDocumentRange(pattern, getMostInnerTypeName(moduleInfoReferenceData.typeName), hyperlink.startPosition, hyperlink.endPosition, ranges); } if (M && (moduleInfoReferenceData.type == MODULE)) { matchAndAddDocumentRange(pattern, moduleInfoReferenceData.name, hyperlink.startPosition, hyperlink.endPosition, ranges); } } } } } if ((ranges != null) && !ranges.isEmpty()) { textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR); Marker.markAll(textArea, ranges); ranges.sort(null); setCaretPositionAndCenter(ranges.get(0)); } return true; } // --- IndexesChangeListener --- // @Override public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (ReferenceData reference : references) { ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)reference; boolean enabled = false; try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map index; String key; switch (moduleInfoReferenceData.type) { case TYPE: index = futureIndexes.get().getIndex("typeDeclarations"); key = reference.typeName; break; case PACKAGE: index = futureIndexes.get().getIndex("packageDeclarations"); key = reference.typeName; break; default: // MODULE index = futureIndexes.get().getIndex("javaModuleDeclarations"); key = reference.name; break; } if ((index != null) && (index.get(key) != null)) { enabled = true; break; } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } if (reference.enabled != enabled) { reference.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } protected static class ModuleInfoReferenceData extends ReferenceData { public int type; public ModuleInfoReferenceData(int type, String typeName, String name, String descriptor, String owner) { super(typeName, name, descriptor, owner); this.type = type; } } public class ModuleInfoFilePrinter extends StringBuilderPrinter { protected HashMap referencesCache = new HashMap<>(); @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {} @Override public void end() { setText(stringBuffer.toString()); initLineNumbers(); } @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { declarations.put(internalTypeName, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, name, descriptor)); super.printDeclaration(type, internalTypeName, name, descriptor); } @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { String key = (type == MODULE) ? name : internalTypeName; ReferenceData reference = referencesCache.get(key); if (reference == null) { reference = new ModuleInfoReferenceData(type, internalTypeName, name, descriptor, ownerInternalName); referencesCache.put(key, reference); references.add(reference); } addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), reference)); super.printReference(type, internalTypeName, name, descriptor, ownerInternalName); } } // https://github.com/bobbylight/RSyntaxTextArea/wiki/Adding-Syntax-Highlighting-for-a-new-Language public static class ModuleInfoTokenMaker extends AbstractTokenMaker { @Override public TokenMap getWordsToHighlight() { TokenMap tokenMap = new TokenMap(); tokenMap.put("exports", Token.RESERVED_WORD); tokenMap.put("module", Token.RESERVED_WORD); tokenMap.put("open", Token.RESERVED_WORD); tokenMap.put("opens", Token.RESERVED_WORD); tokenMap.put("provides", Token.RESERVED_WORD); tokenMap.put("requires", Token.RESERVED_WORD); tokenMap.put("to", Token.RESERVED_WORD); tokenMap.put("transitive", Token.RESERVED_WORD); tokenMap.put("uses", Token.RESERVED_WORD); tokenMap.put("with", Token.RESERVED_WORD); return tokenMap; } @Override public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) { // This assumes all keywords, etc. were parsed as "identifiers." if (tokenType==Token.IDENTIFIER) { int value = wordsToHighlight.get(segment, start, end); if (value != -1) { tokenType = value; } } super.addToken(segment, start, end, tokenType, startOffset); } @Override public Token getTokenList(Segment text, int startTokenType, int startOffset) { resetTokenList(); char[] array = text.array; int offset = text.offset; int end = offset + text.count; int newStartOffset = startOffset - offset; int currentTokenStart = offset; int currentTokenType = startTokenType; for (int i=offset; i> collectionOfFutureIndexes = Collections.emptyList(); public OneTypeReferencePerLinePage(API api, Container.Entry entry) { this.api = api; this.entry = entry; // Load content file & Create hyperlinks StringBuilder sb = new StringBuilder(); int offset = 0; try (BufferedReader br = new BufferedReader(new InputStreamReader(entry.getInputStream()))) { String line; while ((line = br.readLine()) != null) { String trim = line.trim(); if (trim.length() > 0) { int startIndex = offset + line.indexOf(trim); int endIndex = startIndex + trim.length(); String internalTypeName = trim.replace('.', '/'); addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)); } offset += line.length() + 1; sb.append(line).append('\n'); } } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } // Display setText(sb.toString()); } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData; if (data.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x-location.x, y-location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link String internalTypeName = data.internalTypeName; List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, data.internalTypeName); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, data.internalTypeName); } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (Map.Entry entry : hyperlinks.entrySet()) { TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue(); String internalTypeName = entryData.internalTypeName; boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); if (entryData.enabled != enabled) { entryData.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/RoundMarkErrorStrip.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.parser.Parser; import org.fife.ui.rsyntaxtextarea.parser.ParserNotice; import org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice; import org.fife.ui.rtextarea.RTextArea; import javax.swing.*; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.MessageFormat; import java.util.*; import java.util.List; /* * 'private' access prohibit all changes ==> Copy "ErrorStrip" to JD-GUI project just to change the marker. * * JD-GUI uses two workarounds for RSyntaxTextArea: * - org.fife.ui.rtextarea.Marker * - org.jd.gui.view.component.RoundMarkErrorStrip */ /* * 08/10/2009 * * ErrorStrip.java - A component that can visually show Parser messages (syntax * errors, etc.) in an RSyntaxTextArea. * * This library is distributed under a modified BSD license. See the included * RSyntaxTextArea.License.txt file for details. */ /** * A component to sit alongside an {@link RSyntaxTextArea} that displays * colored markers for locations of interest (parser errors, marked * occurrences, etc.).

* * ErrorStrips display ParserNotices from * {@link Parser}s. Currently, the only way to get lines flagged in this * component is to register a Parser on an RSyntaxTextArea and * return ParserNotices for each line to display an icon for. * The severity of each notice must be at least the threshold set by * {@link #setLevelThreshold(ParserNotice.Level)} * to be displayed in this error strip. The default threshold is * {@link ParserNotice.Level#WARNING}.

* * An ErrorStrip can be added to a UI like so: *

 * textArea = createTextArea();
 * textArea.addParser(new MyParser(textArea)); // Identifies lines to display
 * scrollPane = new RTextScrollPane(textArea, true);
 * ErrorStrip es = new ErrorStrip(textArea);
 * JPanel temp = new JPanel(new BorderLayout());
 * temp.add(scrollPane);
 * temp.add(es, BorderLayout.LINE_END);
 * 
* * @author Robert Futrell * @version 0.5 */ /* * Possible improvements: * 1. Handle marked occurrence changes & "mark all" changes separately from * parser changes. For each property change, call a method that removes * the notices being reloaded from the Markers (removing any Markers that * are now "empty"). */ public class RoundMarkErrorStrip extends JComponent { /** * The text area. */ private RSyntaxTextArea textArea; /** * Listens for events in this component. */ private Listener listener; /** * Whether "marked occurrences" in the text area should be shown in this * error strip. */ private boolean showMarkedOccurrences; /** * Whether markers for "mark all" highlights should be shown in this * error strip. */ private boolean showMarkAll; /** * Mapping of colors to brighter colors. This is kept to prevent * unnecessary creation of the same Colors over and over. */ private Map brighterColors; /** * Added for JD-GUI. * * Mapping of colors to darker colors. This is kept to prevent * unnecessary creation of the same Colors over and over. */ private Map darkerColors; /** * Only notices of this severity (or worse) will be displayed in this * error strip. */ private ParserNotice.Level levelThreshold; /** * Whether the caret marker's location should be rendered. */ private boolean followCaret; /** * The color to use for the caret marker. */ private Color caretMarkerColor; /** * Where we paint the caret marker. */ private int caretLineY; /** * The last location of the caret marker. */ private int lastLineY; /** * The preferred width of this component. */ private static final int PREFERRED_WIDTH = 14; private static final String MSG = "org.fife.ui.rsyntaxtextarea.ErrorStrip"; private static final ResourceBundle msg = ResourceBundle.getBundle(MSG); /** * Constructor. * * @param textArea The text area we are examining. */ public RoundMarkErrorStrip(RSyntaxTextArea textArea) { this.textArea = textArea; listener = new Listener(); ToolTipManager.sharedInstance().registerComponent(this); setLayout(null); // Manually layout Markers as they can overlap addMouseListener(listener); setShowMarkedOccurrences(true); setShowMarkAll(true); setLevelThreshold(ParserNotice.Level.WARNING); setFollowCaret(true); setCaretMarkerColor(new Color(0x96c5fe)); } /** * Overridden so we only start listening for parser notices when this * component (and presumably the text area) are visible. */ @Override public void addNotify() { super.addNotify(); textArea.addCaretListener(listener); textArea.addPropertyChangeListener( RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener); textArea.addPropertyChangeListener( RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener); textArea.addPropertyChangeListener( RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener); textArea.addPropertyChangeListener( RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener); refreshMarkers(); } /** * Manually manages layout since this component uses no layout manager. */ @Override public void doLayout() { for (int i=0; i(5); // Usually small } Color brighter = brighterColors.get(c); if (brighter==null) { // Don't use c.brighter() as it doesn't work well for blue, and // also doesn't return something brighter "enough." int r = possiblyBrighter(c.getRed()); int g = possiblyBrighter(c.getGreen()); int b = possiblyBrighter(c.getBlue()); brighter = new Color(r, g, b); brighterColors.put(c, brighter); } return brighter; } /** * Added for JD-GUI. * * Returns a "brighter" color. * * @param c The color. * @return A brighter color. */ private Color getDarkerColor(Color c) { if (darkerColors==null) { darkerColors = new HashMap(5); // Usually small } Color darker = darkerColors.get(c); if (darker==null) { // Don't use c.brighter() as it doesn't work well for blue, and // also doesn't return something brighter "enough." int r = possiblyDarker(c.getRed()); int g = possiblyDarker(c.getGreen()); int b = possiblyDarker(c.getBlue()); darker = new Color(r, g, b); darkerColors.put(c, darker); } return darker; } /** * returns the color to use when painting the caret marker. * * @return The caret marker color. * @see #setCaretMarkerColor(Color) */ public Color getCaretMarkerColor() { return caretMarkerColor; } /** * Returns whether the caret's position should be drawn. * * @return Whether the caret's position should be drawn. * @see #setFollowCaret(boolean) */ public boolean getFollowCaret() { return followCaret; } /** * {@inheritDoc} */ @Override public Dimension getPreferredSize() { int height = textArea.getPreferredScrollableViewportSize().height; return new Dimension(PREFERRED_WIDTH, height); } /** * Returns the minimum severity a parser notice must be for it to be * displayed in this error strip. This will be one of the constants * defined in the ParserNotice class. * * @return The minimum severity. * @see #setLevelThreshold(ParserNotice.Level) */ public ParserNotice.Level getLevelThreshold() { return levelThreshold; } /** * Returns whether "mark all" highlights are shown in this error strip. * * @return Whether markers are shown for "mark all" highlights. * @see #setShowMarkAll(boolean) */ public boolean getShowMarkAll() { return showMarkAll; } /** * Returns whether marked occurrences are shown in this error strip. * * @return Whether marked occurrences are shown. * @see #setShowMarkedOccurrences(boolean) */ public boolean getShowMarkedOccurrences() { return showMarkedOccurrences; } /** * {@inheritDoc} */ @Override public String getToolTipText(MouseEvent e) { String text = null; int line = yToLine(e.getY()); if (line>-1) { text = msg.getString("Line"); text = MessageFormat.format(text, Integer.valueOf(line+1)); } return text; } /** * Returns the y-offset in this component corresponding to a line in the * text component. * * @param line The line. * @return The y-offset. * @see #yToLine(int) */ private int lineToY(int line) { int h = textArea.getVisibleRect().height; float lineCount = textArea.getLineCount(); return (int)(((line-1)/(lineCount-1)) * h) - 2; } /** * Overridden to (possibly) draw the caret's position. * * @param g The graphics context. */ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (caretLineY>-1) { g.setColor(getCaretMarkerColor()); g.fillRect(0, caretLineY, getWidth(), 2); } } /** * Returns a possibly brighter component for a color. * * @param i An RGB component for a color (0-255). * @return A possibly brighter value for the component. */ private static final int possiblyBrighter(int i) { if (i<255) { i += (int)((255-i)*0.6f); } return i; } /** * Returns a possibly darker component for a color. * * @param i An RGB component for a color (0-255). * @return A possibly brighter value for the component. */ private static final int possiblyDarker(int i) { return i -= (int)(i*0.4f); } /** * Refreshes the markers displayed in this error strip. */ private void refreshMarkers() { removeAll(); // listener is removed in Marker.removeNotify() Map markerMap = new HashMap(); List notices = textArea.getParserNotices(); for (ParserNotice notice : notices) { if (notice.getLevel().isEqualToOrWorseThan(levelThreshold) || (notice instanceof TaskNotice)) { Integer key = Integer.valueOf(notice.getLine()); Marker m = markerMap.get(key); if (m==null) { m = new Marker(notice); m.addMouseListener(listener); markerMap.put(key, m); add(m); } else { m.addNotice(notice); } } } if (getShowMarkedOccurrences() && textArea.getMarkOccurrences()) { List occurrences = textArea.getMarkedOccurrences(); addMarkersForRanges(occurrences, markerMap, textArea.getMarkOccurrencesColor()); } if (getShowMarkAll() /*&& textArea.getMarkAll()*/) { Color markAllColor = textArea.getMarkAllHighlightColor(); List ranges = textArea.getMarkAllHighlightRanges(); addMarkersForRanges(ranges, markerMap, markAllColor); } revalidate(); repaint(); } /** * Adds markers for a list of ranges in the document. * * @param ranges The list of ranges in the document. * @param markerMap A mapping from line number to Marker. * @param color The color to use for the markers. */ private void addMarkersForRanges(List ranges, Map markerMap, Color color) { for (DocumentRange range : ranges) { int line = 0; try { line = textArea.getLineOfOffset(range.getStartOffset()); } catch (BadLocationException ble) { // Never happens continue; } ParserNotice notice = new MarkedOccurrenceNotice(range, color); Integer key = Integer.valueOf(line); Marker m = markerMap.get(key); if (m==null) { m = new Marker(notice); m.addMouseListener(listener); markerMap.put(key, m); add(m); } else { if (!m.containsMarkedOccurence()) { m.addNotice(notice); } } } } /** * {@inheritDoc} */ @Override public void removeNotify() { super.removeNotify(); textArea.removeCaretListener(listener); textArea.removePropertyChangeListener( RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener); textArea.removePropertyChangeListener( RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener); textArea.removePropertyChangeListener( RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener); textArea.removePropertyChangeListener( RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener); } /** * Sets the color to use when painting the caret marker. * * @param color The new caret marker color. * @see #getCaretMarkerColor() */ public void setCaretMarkerColor(Color color) { if (color!=null) { caretMarkerColor = color; listener.caretUpdate(null); // Force repaint } } /** * Toggles whether the caret's current location should be drawn. * * @param follow Whether the caret's current location should be followed. * @see #getFollowCaret() */ public void setFollowCaret(boolean follow) { if (followCaret!=follow) { if (followCaret) { repaint(0,caretLineY, getWidth(),2); // Erase } caretLineY = -1; lastLineY = -1; followCaret = follow; listener.caretUpdate(null); // Possibly repaint } } /** * Sets the minimum severity a parser notice must be for it to be displayed * in this error strip. This should be one of the constants defined in * the ParserNotice class. The default value is * {@link ParserNotice.Level#WARNING}. * * @param level The new severity threshold. * @see #getLevelThreshold() * @see ParserNotice */ public void setLevelThreshold(ParserNotice.Level level) { levelThreshold = level; if (isDisplayable()) { refreshMarkers(); } } /** * Sets whether "mark all" highlights are shown in this error strip. * * @param show Whether to show markers for "mark all" highlights. * @see #getShowMarkAll() */ public void setShowMarkAll(boolean show) { if (show!=showMarkAll) { showMarkAll = show; if (isDisplayable()) { // Skip this when we're first created refreshMarkers(); } } } /** * Sets whether marked occurrences are shown in this error strip. * * @param show Whether to show marked occurrences. * @see #getShowMarkedOccurrences() */ public void setShowMarkedOccurrences(boolean show) { if (show!=showMarkedOccurrences) { showMarkedOccurrences = show; if (isDisplayable()) { // Skip this when we're first created refreshMarkers(); } } } /** * Returns the line in the text area corresponding to a y-offset in this * component. * * @param y The y-offset. * @return The line. * @see #lineToY(int) */ private final int yToLine(int y) { int line = -1; int h = textArea.getVisibleRect().height; if (y-1) { try { int offs = textArea.getLineStartOffset(line); textArea.setCaretPosition(offs); } catch (BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(textArea); } } } public void propertyChange(PropertyChangeEvent e) { String propName = e.getPropertyName(); // If they change whether marked occurrences are visible in editor if (RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY.equals(propName)) { if (getShowMarkedOccurrences()) { refreshMarkers(); } } // If parser notices changed. // TODO: Don't update "mark all/occurrences" markers. else if (RSyntaxTextArea.PARSER_NOTICES_PROPERTY.equals(propName)) { refreshMarkers(); } // If marked occurrences changed. // TODO: Only update "mark occurrences" markers, not all of them. else if (RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY. equals(propName)) { if (getShowMarkedOccurrences()) { refreshMarkers(); } } // If "mark all" occurrences changed. // TODO: Only update "mark all" markers, not all of them. else if (RTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY. equals(propName)) { if (getShowMarkAll()) { refreshMarkers(); } } } } /** * A notice that wraps a "marked occurrence." */ private class MarkedOccurrenceNotice implements ParserNotice { private DocumentRange range; private Color color; public MarkedOccurrenceNotice(DocumentRange range, Color color) { this.range = range; this.color = color; } public int compareTo(ParserNotice other) { return 0; // Value doesn't matter } public boolean containsPosition(int pos) { return pos>=range.getStartOffset() && pos notices; public Marker(ParserNotice notice) { notices = new ArrayList(1); // Usually just 1 addNotice(notice); setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); setSize(getPreferredSize()); ToolTipManager.sharedInstance().registerComponent(this); } public void addNotice(ParserNotice notice) { notices.add(notice); } public boolean containsMarkedOccurence() { boolean result = false; for (int i=0; i 1 StringBuilder sb = new StringBuilder(""); sb.append(msg.getString("MultipleMarkers")); sb.append("
"); for (int i=0; i"); } text = sb.toString(); } return text; } protected void mouseClicked(MouseEvent e) { ParserNotice pn = notices.get(0); int offs = pn.getOffset(); int len = pn.getLength(); if (offs>-1 && len>-1) { // These values are optional textArea.setSelectionStart(offs); textArea.setSelectionEnd(offs+len); } else { int line = pn.getLine(); try { offs = textArea.getLineStartOffset(line); textArea.setCaretPosition(offs); } catch (BadLocationException ble) { // Never happens UIManager.getLookAndFeel().provideErrorFeedback(textArea); } } } @Override protected void paintComponent(Graphics g) { // TODO: Give "priorities" and always pick color of a notice with // highest priority (e.g. parsing errors will usually be red). Color color = getColor(); if (color==null) { color = Color.GRAY; } Color brighterColor = getBrighterColor(color); Color darkerColor = getDarkerColor(color); int w = getWidth(); int h = getHeight(); // Draw background g.setColor(color); g.fillRect(0,0, w,h); // Draw border w--; h--; g.setColor(darkerColor); g.drawLine(w,0, w,h); g.drawRect(0,h, w,h); g.setColor(brighterColor); g.drawLine(0,0, w,0); g.drawRect(0,0, 0,h); } @Override public void removeNotify() { super.removeNotify(); ToolTipManager.sharedInstance().unregisterComponent(this); removeMouseListener(listener); } public void updateLocation() { int line = notices.get(0).getLine(); int y = lineToY(line); setLocation(2, y); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/TextPage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.jd.gui.api.API; import org.jd.gui.api.feature.ContentCopyable; import org.jd.gui.api.feature.ContentSavable; import org.jd.gui.api.feature.ContentSelectable; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.io.NewlineOutputStream; import java.awt.datatransfer.StringSelection; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; public class TextPage extends AbstractTextPage implements ContentCopyable, ContentSelectable, ContentSavable { // --- ContentCopyable --- // @Override public void copy() { if (textArea.getSelectionStart() == textArea.getSelectionEnd()) { getToolkit().getSystemClipboard().setContents(new StringSelection(""), null); } else { textArea.copyAsStyledText(); } } // --- ContentSelectable --- // @Override public void selectAll() { textArea.selectAll(); } // --- ContentSavable --- // @Override public String getFileName() { return "file.txt"; } @Override public void save(API api, OutputStream os) { try (OutputStreamWriter writer = new OutputStreamWriter(new NewlineOutputStream(os), "UTF-8")) { writer.write(textArea.getText()); } catch (IOException e) { assert ExceptionUtil.printStackTrace(e); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/TypePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rtextarea.Marker; import org.jd.gui.api.API; import org.jd.gui.api.feature.FocusedTypeGettable; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.feature.UriOpenable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.api.model.Type; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import org.jd.gui.util.matcher.DescriptorMatcher; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.List; import java.util.concurrent.Future; import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; public abstract class TypePage extends CustomLineNumbersPage implements UriGettable, UriOpenable, IndexesChangeListener, FocusedTypeGettable { protected API api; protected Container.Entry entry; protected Collection> collectionOfFutureIndexes = Collections.emptyList(); protected HashMap declarations = new HashMap<>(); protected TreeMap typeDeclarations = new TreeMap<>(); protected ArrayList references = new ArrayList<>(); protected ArrayList strings = new ArrayList<>(); public TypePage(API api, Container.Entry entry) { // Init attributes this.api = api; this.entry = entry; } @Override protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((HyperlinkReferenceData)hyperlinkData).reference.enabled; } @Override @SuppressWarnings("unchecked") protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { HyperlinkReferenceData hyperlinkReferenceData = (HyperlinkReferenceData)hyperlinkData; if (hyperlinkReferenceData.reference.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x - location.x, y - location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link ReferenceData reference = hyperlinkReferenceData.reference; String typeName = reference.typeName; List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, typeName); String fragment = typeName; if (reference.name != null) { fragment += '-' + reference.name; } if (reference.descriptor != null) { fragment += '-' + reference.descriptor; } if (entries.contains(entry)) { api.openURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), fragment)); } else { String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, fragment); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, fragment); } } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // @Override public URI getUri() { return entry.getUri(); } // --- UriOpenable --- // /** * @param uri for URI format, @see jd.gui.api.feature.UriOpenable */ @Override public boolean openUri(URI uri) { ArrayList ranges = new ArrayList<>(); String fragment = uri.getFragment(); String query = uri.getQuery(); Marker.clearMarkAllHighlights(textArea); if (fragment != null) { matchFragmentAndAddDocumentRange(fragment, declarations, ranges); } if (query != null) { Map parameters = parseQuery(query); if (parameters.containsKey("lineNumber")) { String lineNumber = parameters.get("lineNumber"); try { goToLineNumber(Integer.parseInt(lineNumber)); return true; } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else if (parameters.containsKey("position")) { String position = parameters.get("position"); try { int pos = Integer.parseInt(position); if (textArea.getDocument().getLength() > pos) { ranges.add(new DocumentRange(pos, pos)); } } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else { matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges); } } if ((ranges != null) && !ranges.isEmpty()) { textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR); Marker.markAll(textArea, ranges); ranges.sort(null); setCaretPositionAndCenter(ranges.get(0)); } return true; } public static void matchFragmentAndAddDocumentRange(String fragment, HashMap declarations, List ranges) { if ((fragment.indexOf('?') != -1) || (fragment.indexOf('*') != -1)) { // Unknown type and/or descriptor ==> Select all and scroll to the first one int lastDash = fragment.lastIndexOf('-'); if (lastDash == -1) { // Search types String slashAndTypeName = fragment.substring(1); String typeName = fragment.substring(2); for (Map.Entry entry : declarations.entrySet()) { if (entry.getKey().endsWith(slashAndTypeName) || entry.getKey().equals(typeName)) { ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition)); } } } else { String prefix = fragment.substring(0, lastDash+1); String suffix = fragment.substring(lastDash+1); BiFunction matchDescriptors; if (suffix.charAt(0) == '(') { matchDescriptors = DescriptorMatcher::matchMethodDescriptors; } else { matchDescriptors = DescriptorMatcher::matchFieldDescriptors; } if (fragment.charAt(0) == '*') { // Unknown type String slashAndTypeNameAndName = prefix.substring(1); String typeNameAndName = prefix.substring(2); for (Map.Entry entry : declarations.entrySet()) { String key = entry.getKey(); if ((key.indexOf(slashAndTypeNameAndName) != -1) || (key.startsWith(typeNameAndName))) { int index = key.lastIndexOf('-') + 1; if (matchDescriptors.apply(suffix, key.substring(index))) { ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition)); } } } } else { // Known type for (Map.Entry entry : declarations.entrySet()) { String key = entry.getKey(); if (key.startsWith(prefix)) { int index = key.lastIndexOf('-') + 1; if (matchDescriptors.apply(suffix, key.substring(index))) { ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition)); } } } } } } else { // Known type and descriptor ==> Search and high light item DeclarationData data = declarations.get(fragment); if (data != null) { ranges.add(new DocumentRange(data.startPosition, data.endPosition)); } else if (fragment.endsWith("--()V")) { // 'static' bloc not found ==> Select type declaration String typeName = fragment.substring(0, fragment.indexOf('-')); data = declarations.get(typeName); ranges.add(new DocumentRange(data.startPosition, data.endPosition)); } } } public static void matchQueryAndAddDocumentRange( Map parameters, HashMap declarations, TreeMap hyperlinks, ArrayList strings, List ranges) { String highlightFlags = parameters.get("highlightFlags"); String highlightPattern = parameters.get("highlightPattern"); if ((highlightFlags != null) && (highlightPattern != null)) { String highlightScope = parameters.get("highlightScope"); String regexp = createRegExp(highlightPattern); Pattern pattern = Pattern.compile(regexp + ".*"); if (highlightFlags.indexOf('s') != -1) { // Highlight strings Pattern patternForString = Pattern.compile(regexp); for (StringData data : strings) { if (matchScope(highlightScope, data.owner)) { Matcher matcher = patternForString.matcher(data.text); int offset = data.startPosition; while(matcher.find()) { ranges.add(new DocumentRange(offset + matcher.start(), offset + matcher.end())); } } } } boolean t = (highlightFlags.indexOf('t') != -1); // Highlight types boolean f = (highlightFlags.indexOf('f') != -1); // Highlight fields boolean m = (highlightFlags.indexOf('m') != -1); // Highlight methods boolean c = (highlightFlags.indexOf('c') != -1); // Highlight constructors if (highlightFlags.indexOf('d') != -1) { // Highlight declarations for (Map.Entry entry : declarations.entrySet()) { DeclarationData declaration = entry.getValue(); if (matchScope(highlightScope, declaration.typeName)) { if ((t && declaration.isAType()) || (c && declaration.isAConstructor())) { matchAndAddDocumentRange(pattern, getMostInnerTypeName(declaration.typeName), declaration.startPosition, declaration.endPosition, ranges); } if ((f && declaration.isAField()) || (m && declaration.isAMethod())) { matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges); } } } } if (highlightFlags.indexOf('r') != -1) { // Highlight references for (Map.Entry entry : hyperlinks.entrySet()) { HyperlinkData hyperlink = entry.getValue(); ReferenceData reference = ((HyperlinkReferenceData)hyperlink).reference; if (matchScope(highlightScope, reference.owner)) { if ((t && reference.isAType()) || (c && reference.isAConstructor())) { matchAndAddDocumentRange(pattern, getMostInnerTypeName(reference.typeName), hyperlink.startPosition, hyperlink.endPosition, ranges); } if ((f && reference.isAField()) || (m && reference.isAMethod())) { matchAndAddDocumentRange(pattern, reference.name, hyperlink.startPosition, hyperlink.endPosition, ranges); } } } } } } public static boolean matchScope(String scope, String type) { if ((scope == null) || scope.isEmpty()) return true; if (scope.charAt(0) == '*') return type.endsWith(scope.substring(1)) || type.equals(scope.substring(2)); return type.equals(scope); } public static void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List ranges) { if (pattern.matcher(text).matches()) { ranges.add(new DocumentRange(start, end)); } } public static String getMostInnerTypeName(String typeName) { int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); return typeName.substring(lastIndex); } // --- FocusedTypeGettable --- // @Override public String getFocusedTypeName() { Map.Entry entry = typeDeclarations.floorEntry(textArea.getCaretPosition()); if (entry != null) { DeclarationData data = entry.getValue(); if (data != null) { return data.typeName; } } return null; } @Override public Container.Entry getEntry() { return entry; } // --- IndexesChangeListener --- // @Override public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (ReferenceData reference : references) { String typeName = reference.typeName; boolean enabled; if (reference.name == null) { enabled = false; try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map index = futureIndexes.get().getIndex("typeDeclarations"); if ((index != null) && (index.get(typeName) != null)) { enabled = true; break; } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } } else { try { // Recursive search typeName = searchTypeHavingMember(typeName, reference.name, reference.descriptor, entry); if (typeName != null) { // Replace type with the real type having the referenced member reference.typeName = typeName; enabled = true; } else { enabled = false; } } catch (Error e) { // Catch StackOverflowError or OutOfMemoryError assert ExceptionUtil.printStackTrace(e); enabled = false; } } if (reference.enabled != enabled) { reference.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } @SuppressWarnings("unchecked") protected String searchTypeHavingMember(String typeName, String name, String descriptor, Container.Entry entry) { ArrayList entries = new ArrayList<>(); try { for (Future futureIndexes : collectionOfFutureIndexes) { if (futureIndexes.isDone()) { Map index = futureIndexes.get().getIndex("typeDeclarations"); if (index != null) { Collection collection = index.get(typeName); if (collection != null) { entries.addAll(collection); } } } } } catch (Exception e) { assert ExceptionUtil.printStackTrace(e); } String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry e : entries) { if (e.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(e); } } if (sameContainerEntries.size() > 0) { return searchTypeHavingMember(typeName, name, descriptor, sameContainerEntries); } else { return searchTypeHavingMember(typeName, name, descriptor, entries); } } protected String searchTypeHavingMember(String typeName, String name, String descriptor, List entries) { for (Container.Entry entry : entries) { Type type = api.getTypeFactory(entry).make(api, entry, typeName); if (type != null) { if (descriptor.indexOf('(') == -1) { // Search a field for (Type.Field field : type.getFields()) { if (field.getName().equals(name) && DescriptorMatcher.matchFieldDescriptors(field.getDescriptor(), descriptor)) { // Field found return typeName; } } } else { // Search a method for (Type.Method method : type.getMethods()) { if (method.getName().equals(name) && DescriptorMatcher.matchMethodDescriptors(method.getDescriptor(), descriptor)) { // Method found return typeName; } } } // Not found -> Search in super type String typeOwnerName = searchTypeHavingMember(type.getSuperName(), name, descriptor, entry); if (typeOwnerName != null) { return typeOwnerName; } } } return null; } public static class StringData { int startPosition; int endPosition; String text; String owner; public StringData(int startPosition, int length, String text, String owner) { this.startPosition = startPosition; this.endPosition = startPosition + length; this.text = text; this.owner = owner; } } public static class DeclarationData { int startPosition; int endPosition; String typeName; /** * Field or method name or null for type */ String name; String descriptor; public DeclarationData(int startPosition, int length, String typeName, String name, String descriptor) { this.startPosition = startPosition; this.endPosition = startPosition + length; this.typeName = typeName; this.name = name; this.descriptor = descriptor; } public boolean isAType() { return name == null; } public boolean isAField() { return (descriptor != null) && descriptor.charAt(0) != '('; } public boolean isAMethod() { return (descriptor != null) && descriptor.charAt(0) == '('; } public boolean isAConstructor() { return "".equals(name); } } public static class HyperlinkReferenceData extends HyperlinkData { public ReferenceData reference; public HyperlinkReferenceData(int startPosition, int length, ReferenceData reference) { super(startPosition, startPosition+length); this.reference = reference; } } protected static class ReferenceData { public String typeName; /** * Field or method name or null for type */ public String name; /** * Field or method descriptor or null for type */ public String descriptor; /** * Internal type name containing reference or null for "import" statement. * Used to high light items matching with URI like "file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=type". */ public String owner; /** * "Enabled" flag for link of reference */ public boolean enabled = false; public ReferenceData(String typeName, String name, String descriptor, String owner) { this.typeName = typeName; this.name = name; this.descriptor = descriptor; this.owner = owner; } boolean isAType() { return name == null; } boolean isAField() { return (descriptor != null) && descriptor.charAt(0) != '('; } boolean isAMethod() { return (descriptor != null) && descriptor.charAt(0) == '('; } boolean isAConstructor() { return "".equals(name); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rtextarea.Marker; import org.jd.gui.util.exception.ExceptionUtil; import java.net.URI; import java.util.ArrayList; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Page containing type references (Hyperlinks to pages of type) */ public abstract class TypeReferencePage extends HyperlinkPage { // --- UriOpenable --- // public boolean openUri(URI uri) { ArrayList ranges = new ArrayList<>(); String query = uri.getQuery(); Marker.clearMarkAllHighlights(textArea); if (query != null) { Map parameters = parseQuery(query); if (parameters.containsKey("lineNumber")) { String lineNumber = parameters.get("lineNumber"); try { goToLineNumber(Integer.parseInt(lineNumber)); return true; } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else if (parameters.containsKey("position")) { String position = parameters.get("position"); try { int pos = Integer.parseInt(position); if (textArea.getDocument().getLength() > pos) { ranges.add(new DocumentRange(pos, pos)); } } catch (NumberFormatException e) { assert ExceptionUtil.printStackTrace(e); } } else { String highlightFlags = parameters.get("highlightFlags"); String highlightPattern = parameters.get("highlightPattern"); if ((highlightFlags != null) && (highlightPattern != null)) { String regexp = createRegExp(highlightPattern); if (highlightFlags.indexOf('s') != -1) { // Highlight strings Pattern pattern = Pattern.compile(regexp); Matcher matcher = pattern.matcher(textArea.getText()); while (matcher.find()) { ranges.add(new DocumentRange(matcher.start(), matcher.end())); } } if ((highlightFlags.indexOf('t') != -1) && (highlightFlags.indexOf('r') != -1)) { // Highlight type references Pattern pattern = Pattern.compile(regexp + ".*"); for (Map.Entry entry : hyperlinks.entrySet()) { TypeHyperlinkData hyperlink = (TypeHyperlinkData)entry.getValue(); String name = getMostInnerTypeName(hyperlink.internalTypeName); if (pattern.matcher(name).matches()) { ranges.add(new DocumentRange(hyperlink.startPosition, hyperlink.endPosition)); } } } } } } if ((ranges != null) && !ranges.isEmpty()) { textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR); Marker.markAll(textArea, ranges); ranges.sort(null); setCaretPositionAndCenter(ranges.get(0)); } return false; } public String getMostInnerTypeName(String typeName) { int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1; int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1; int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex); return typeName.substring(lastIndex); } public static class TypeHyperlinkData extends HyperlinkData { public boolean enabled; public String internalTypeName; TypeHyperlinkData(int startPosition, int endPosition, String internalTypeName) { super(startPosition, endPosition); this.enabled = false; this.internalTypeName = internalTypeName; } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import org.jd.gui.util.io.TextReader; import org.jd.gui.util.xml.AbstractXmlPathFinder; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.List; import java.util.concurrent.Future; public class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { protected API api; protected Container.Entry entry; protected Collection> collectionOfFutureIndexes; public WebXmlFilePage(API api, Container.Entry entry) { this.api = api; this.entry = entry; // Load content file String text = TextReader.getText(entry.getInputStream()); // Create hyperlinks new PathFinder().find(text); // Display setText(text); } public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData; if (data.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x-location.x, y-location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link if (hyperlinkData instanceof PathHyperlinkData) { PathHyperlinkData d = (PathHyperlinkData)hyperlinkData; String path = d.path; Container.Entry entry = searchEntry(this.entry.getContainer().getRoot(), path); if (entry != null) { api.openURI(x, y, Collections.singletonList(entry), null, path); } } else { String internalTypeName = data.internalTypeName; List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, data.internalTypeName); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, data.internalTypeName); } } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } public static Container.Entry searchEntry(Container.Entry parent, String path) { if (path.charAt(0) == '/') path = path.substring(1); return recursiveSearchEntry(parent, path); } public static Container.Entry recursiveSearchEntry(Container.Entry parent, String path) { Container.Entry entry = null; for (Container.Entry child : parent.getChildren()) { if (path.equals(child.getPath())) { entry = child; break; } } if (entry != null) { return entry; } else { for (Container.Entry child : parent.getChildren()) { if (path.startsWith(child.getPath() + '/')) { entry = child; break; } } return (entry != null) ? searchEntry(entry, path) : null; } } // --- UriGettable --- // public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (Map.Entry entry : hyperlinks.entrySet()) { TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue(); boolean enabled; if (data instanceof PathHyperlinkData) { PathHyperlinkData d = (PathHyperlinkData)data; enabled = searchEntry(this.entry.getContainer().getRoot(), d.path) != null; } else { String internalTypeName = data.internalTypeName; enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); } if (data.enabled != enabled) { data.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } public static class PathHyperlinkData extends TypeHyperlinkData { public boolean enabled; public String path; PathHyperlinkData(int startPosition, int endPosition, String path) { super(startPosition, endPosition, null); this.enabled = false; this.path = path; } } protected static List typeHyperlinkPaths = Arrays.asList( "web-app/filter/filter-class", "web-app/listener/listener-class", "web-app/servlet/servlet-class"); protected static List pathHyperlinkPaths = Arrays.asList( "web-app/jsp-config/taglib/taglib-location", "web-app/welcome-file-list/welcome-file", "web-app/login-config/form-login-config/form-login-page", "web-app/login-config/form-login-config/form-error-page", "web-app/jsp-config/jsp-property-group/include-prelude", "web-app/jsp-config/jsp-property-group/include-coda"); protected static List hyperlinkPaths = new ArrayList<>(typeHyperlinkPaths.size() + pathHyperlinkPaths.size()); static { hyperlinkPaths.addAll(typeHyperlinkPaths); hyperlinkPaths.addAll(pathHyperlinkPaths); } public class PathFinder extends AbstractXmlPathFinder { public PathFinder() { super(hyperlinkPaths); } public void handle(String path, String text, int position) { String trim = text.trim(); if (trim != null) { int startIndex = position + text.indexOf(trim); int endIndex = startIndex + trim.length(); if (pathHyperlinkPaths.contains(path)) { addHyperlink(new PathHyperlinkData(startIndex, endIndex, trim)); } else { String internalTypeName = trim.replace('.', '/'); addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName)); } } } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/component/XmlFilePage.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.component; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.jd.gui.api.API; import org.jd.gui.api.feature.IndexesChangeListener; import org.jd.gui.api.feature.UriGettable; import org.jd.gui.api.model.Container; import org.jd.gui.api.model.Indexes; import org.jd.gui.util.exception.ExceptionUtil; import org.jd.gui.util.index.IndexesUtil; import org.jd.gui.util.io.TextReader; import java.awt.*; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; public class XmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { protected API api; protected Container.Entry entry; protected Collection> collectionOfFutureIndexes; public XmlFilePage(API api, Container.Entry entry) { this.api = api; this.entry = entry; // Load content file String text = TextReader.getText(entry.getInputStream()); // Create hyperlinks Pattern pattern = Pattern.compile("(?s)<\\s*bean[^<]+class\\s*=\\s*\"([^\"]*)\""); Matcher matcher = pattern.matcher(textArea.getText()); while (matcher.find()) { // Spring type reference found String value = matcher.group(1); String trim = value.trim(); if (trim != null) { int startIndex = matcher.start(1) - 1; int endIndex = startIndex + value.length() + 2; String internalTypeName = trim.replace('.', '/'); addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName)); } } // Display setText(text); } public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; } protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; } protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) { TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData; if (data.enabled) { try { // Save current position in history Point location = textArea.getLocationOnScreen(); int offset = textArea.viewToModel(new Point(x-location.x, y-location.y)); URI uri = entry.getUri(); api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null)); // Open link String internalTypeName = data.internalTypeName; List entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName); String rootUri = entry.getContainer().getRoot().getUri().toString(); ArrayList sameContainerEntries = new ArrayList<>(); for (Container.Entry entry : entries) { if (entry.getUri().toString().startsWith(rootUri)) { sameContainerEntries.add(entry); } } if (sameContainerEntries.size() > 0) { api.openURI(x, y, sameContainerEntries, null, data.internalTypeName); } else if (entries.size() > 0) { api.openURI(x, y, entries, null, data.internalTypeName); } } catch (URISyntaxException e) { assert ExceptionUtil.printStackTrace(e); } } } // --- UriGettable --- // public URI getUri() { return entry.getUri(); } // --- ContentSavable --- // public String getFileName() { String path = entry.getPath(); int index = path.lastIndexOf('/'); return path.substring(index+1); } // --- IndexesChangeListener --- // public void indexesChanged(Collection> collectionOfFutureIndexes) { // Update the list of containers this.collectionOfFutureIndexes = collectionOfFutureIndexes; // Refresh links boolean refresh = false; for (Map.Entry entry : hyperlinks.entrySet()) { TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue(); String internalTypeName = data.internalTypeName; boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName); if (data.enabled != enabled) { data.enabled = enabled; refresh = true; } } if (refresh) { textArea.repaint(); } } } ================================================ FILE: services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java ================================================ /* * Copyright (c) 2008-2019 Emmanuel Dupuy. * This project is distributed under the GPLv3 license. * This is a Copyleft license that gives the user the right to use, * copy and modify the code freely for non-commercial purposes. */ package org.jd.gui.view.data; import org.jd.gui.api.model.TreeNodeData; import javax.swing.*; public class TreeNodeBean implements TreeNodeData { protected String label; protected String tip; protected Icon icon; protected Icon openIcon; public TreeNodeBean(String label, Icon icon) { this.label = label; this.icon = icon; } public TreeNodeBean(String label, String tip, Icon icon) { this.label = label; this.tip = tip; this.icon = icon; } public TreeNodeBean(String label, Icon icon, Icon openIcon) { this.label = label; this.icon = icon; this.openIcon = openIcon; } public TreeNodeBean(String label, String tip, Icon icon, Icon openIcon) { this.label = label; this.tip = tip; this.icon = icon; this.openIcon = openIcon; } public void setLabel(String label) { this.label = label; } public void setTip(String tip) { this.tip = tip; } public void setIcon(Icon icon) { this.icon = icon; } public void setOpenIcon(Icon openIcon) { this.openIcon = openIcon; } @Override public String getLabel() { return label; } @Override public String getTip() { return tip; } @Override public Icon getIcon() { return icon; } @Override public Icon getOpenIcon() { return openIcon; } } ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.ContainerFactory ================================================ # Order is important : 'GenericContainerFactoryProvider' must be the last org.jd.gui.service.container.KarContainerFactoryProvider org.jd.gui.service.container.JavaModuleContainerFactoryProvider org.jd.gui.service.container.EarContainerFactoryProvider org.jd.gui.service.container.WarContainerFactoryProvider org.jd.gui.service.container.JarContainerFactoryProvider org.jd.gui.service.container.GenericContainerFactoryProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.ContextualActionsFactory ================================================ org.jd.gui.service.actions.CopyQualifiedNameContextualActionsFactory ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.FileLoader ================================================ org.jd.gui.service.fileloader.AarFileLoaderProvider org.jd.gui.service.fileloader.ClassFileLoaderProvider org.jd.gui.service.fileloader.EarFileLoaderProvider org.jd.gui.service.fileloader.JarFileLoaderProvider org.jd.gui.service.fileloader.JavaFileLoaderProvider org.jd.gui.service.fileloader.JavaModuleFileLoaderProvider org.jd.gui.service.fileloader.KarFileLoaderProvider org.jd.gui.service.fileloader.LogFileLoaderProvider org.jd.gui.service.fileloader.WarFileLoaderProvider org.jd.gui.service.fileloader.ZipFileLoaderProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.Indexer ================================================ org.jd.gui.service.indexer.DirectoryIndexerProvider org.jd.gui.service.indexer.ClassFileIndexerProvider org.jd.gui.service.indexer.EjbJarXmlFileIndexerProvider org.jd.gui.service.indexer.JavaFileIndexerProvider org.jd.gui.service.indexer.JavaModuleFileIndexerProvider org.jd.gui.service.indexer.JavaModuleInfoFileIndexerProvider org.jd.gui.service.indexer.MetainfServiceFileIndexerProvider org.jd.gui.service.indexer.TextFileIndexerProvider org.jd.gui.service.indexer.WebXmlFileIndexerProvider org.jd.gui.service.indexer.ZipFileIndexerProvider org.jd.gui.service.indexer.XmlBasedFileIndexerProvider org.jd.gui.service.indexer.XmlFileIndexerProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.PasteHandler ================================================ org.jd.gui.service.pastehandler.LogPasteHandler ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.PreferencesPanel ================================================ org.jd.gui.service.preferencespanel.DirectoryIndexerPreferencesProvider org.jd.gui.service.preferencespanel.ClassFileSaverPreferencesProvider org.jd.gui.service.preferencespanel.ClassFileDecompilerPreferencesProvider org.jd.gui.service.preferencespanel.ViewerPreferencesProvider org.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.SourceLoader ================================================ org.jd.gui.service.sourceloader.MavenOrgSourceLoaderProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.SourceSaver ================================================ org.jd.gui.service.sourcesaver.ClassFileSourceSaverProvider org.jd.gui.service.sourcesaver.DirectorySourceSaverProvider org.jd.gui.service.sourcesaver.FileSourceSaverProvider org.jd.gui.service.sourcesaver.PackageSourceSaverProvider org.jd.gui.service.sourcesaver.ZipFileSourceSaverProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.TreeNodeFactory ================================================ org.jd.gui.service.treenode.ClassesDirectoryTreeNodeFactoryProvider org.jd.gui.service.treenode.ClassFileTreeNodeFactoryProvider org.jd.gui.service.treenode.CssFileTreeNodeFactoryProvider org.jd.gui.service.treenode.DirectoryTreeNodeFactoryProvider org.jd.gui.service.treenode.DtdFileTreeNodeFactoryProvider org.jd.gui.service.treenode.EarFileTreeNodeFactoryProvider org.jd.gui.service.treenode.EjbJarXmlFileTreeNodeFactoryProvider org.jd.gui.service.treenode.FileTreeNodeFactoryProvider org.jd.gui.service.treenode.HtmlFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JarFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JavaFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JavascriptFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JavaModuleFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JavaModulePackageTreeNodeFactoryProvider org.jd.gui.service.treenode.JsonFileTreeNodeFactoryProvider org.jd.gui.service.treenode.JspFileTreeNodeFactoryProvider org.jd.gui.service.treenode.KarFileTreeNodeFactoryProvider org.jd.gui.service.treenode.ManifestFileTreeNodeFactoryProvider org.jd.gui.service.treenode.MetainfDirectoryTreeNodeFactoryProvider org.jd.gui.service.treenode.MetainfServiceFileTreeNodeFactoryProvider org.jd.gui.service.treenode.ModuleInfoFileTreeNodeFactoryProvider org.jd.gui.service.treenode.PackageTreeNodeFactoryProvider org.jd.gui.service.treenode.PropertiesFileTreeNodeFactoryProvider org.jd.gui.service.treenode.SqlFileTreeNodeFactoryProvider org.jd.gui.service.treenode.SpiFileTreeNodeFactoryProvider org.jd.gui.service.treenode.TextFileTreeNodeFactoryProvider org.jd.gui.service.treenode.WarFileTreeNodeFactoryProvider org.jd.gui.service.treenode.WarPackageTreeNodeFactoryProvider org.jd.gui.service.treenode.WebinfLibDirectoryTreeNodeFactoryProvider org.jd.gui.service.treenode.WebXmlFileTreeNodeFactoryProvider org.jd.gui.service.treenode.XmlBasedFileTreeNodeFactoryProvider org.jd.gui.service.treenode.XmlFileTreeNodeFactoryProvider org.jd.gui.service.treenode.ZipFileTreeNodeFactoryProvider org.jd.gui.service.treenode.ImageFileTreeNodeFactoryProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.TypeFactory ================================================ org.jd.gui.service.type.ClassFileTypeFactoryProvider org.jd.gui.service.type.JavaFileTypeFactoryProvider ================================================ FILE: services/src/main/resources/META-INF/services/org.jd.gui.spi.UriLoader ================================================ org.jd.gui.service.uriloader.FileUriLoaderProvider ================================================ FILE: services/src/main/resources/rsyntaxtextarea/RSyntaxTextArea_License.txt ================================================ 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. ================================================ FILE: services/src/main/resources/rsyntaxtextarea/themes/eclipse.xml ================================================