From 9b89bda4158e9ea720b1a817467256286c91428f Mon Sep 17 00:00:00 2001 From: emmanue1 Date: Sun, 7 Apr 2019 18:15:54 +0200 Subject: [PATCH] Convert Groovy classes to Java classes --- .../java/org/jd/gui/spi/PreferencesPanel.java | 3 + app/build.gradle | 7 +- app/src/main/groovy/org/jd/gui/App.groovy | 56 -- .../main/groovy/org/jd/gui/Constants.groovy | 23 - app/src/main/groovy/org/jd/gui/OsxApp.groovy | 38 - .../jd/gui/controller/GoToController.groovy | 27 - .../jd/gui/controller/MainController.groovy | 565 ------------- .../gui/controller/OpenTypeController.groovy | 241 ------ .../OpenTypeHierarchyController.groovy | 86 -- .../controller/PreferencesController.groovy | 28 - .../SaveAllSourcesController.groovy | 90 --- .../SearchInConstantPoolsController.groovy | 437 ---------- .../SelectLocationController.groovy | 166 ---- .../model/configuration/Configuration.groovy | 33 - .../container/FilteredContainerWrapper.groovy | 109 --- .../org/jd/gui/model/history/History.groovy | 80 -- .../ContextualActionsFactoryService.groovy | 68 -- .../ConfigurationPersisterService.groovy | 17 - .../ConfigurationXmlPersisterProvider.groovy | 150 ---- .../container/ContainerFactoryService.groovy | 23 - .../service/extension/ExtensionService.groovy | 55 -- .../fileloader/FileLoaderService.groovy | 34 - .../gui/service/indexer/IndexerService.groovy | 88 --- .../ContainerPanelFactoryProvider.groovy | 130 --- .../mainpanel/PanelFactoryService.groovy | 34 - .../pastehandler/PasteHandlerService.groovy | 25 - .../service/platform/PlatformService.groovy | 31 - .../PreferencesPanelService.groovy | 27 - ...UISingleInstancePreferencesProvider.groovy | 55 -- .../UITabsPreferencesProvider.groovy | 58 -- .../sourcesaver/SourceSaverService.groovy | 88 --- .../treenode/TreeNodeFactoryService.groovy | 89 --- .../service/type/TypeFactoryService.groovy | 95 --- .../service/uriloader/UriLoaderService.groovy | 31 - .../net/InterProcessCommunications.groovy | 31 - .../groovy/org/jd/gui/util/net/UriUtil.groovy | 47 -- .../org/jd/gui/view/GoToDescription.groovy | 41 - .../groovy/org/jd/gui/view/GoToView.groovy | 123 --- .../org/jd/gui/view/MainDescription.groovy | 313 -------- .../groovy/org/jd/gui/view/MainView.groovy | 332 -------- .../jd/gui/view/OpenTypeDescription.groovy | 55 -- .../view/OpenTypeHierarchyDescription.groovy | 42 - .../jd/gui/view/OpenTypeHierarchyView.groovy | 332 -------- .../org/jd/gui/view/OpenTypeView.groovy | 202 ----- .../jd/gui/view/PreferencesDescription.groovy | 41 - .../org/jd/gui/view/PreferencesView.groovy | 142 ---- .../gui/view/SaveAllSourcesDescription.groovy | 31 - .../org/jd/gui/view/SaveAllSourcesView.groovy | 85 -- .../SearchInConstantPoolsDescription.groovy | 89 --- .../gui/view/SearchInConstantPoolsView.groovy | 312 -------- .../org/jd/gui/view/SelectLocationView.groovy | 226 ------ .../org/jd/gui/view/component/List.groovy | 97 --- .../org/jd/gui/view/component/Tree.groovy | 25 - .../component/panel/MainTabbedPanel.groovy | 180 ----- .../view/component/panel/TabbedPanel.groovy | 262 ------ .../component/panel/TreeTabbedPanel.groovy | 287 ------- .../renderer/OpenTypeListCellRenderer.groovy | 98 --- .../gui/view/renderer/TreeNodeRenderer.groovy | 89 --- app/src/main/java/org/jd/gui/App.java | 89 +++ app/src/main/java/org/jd/gui/Constants.java | 23 + app/src/main/java/org/jd/gui/OsxApp.java | 27 + .../jd/gui/controller/AboutController.java | 26 + .../org/jd/gui/controller/GoToController.java | 29 + .../org/jd/gui/controller/MainController.java | 653 +++++++++++++++ .../jd/gui/controller/OpenTypeController.java | 275 +++++++ .../OpenTypeHierarchyController.java | 85 ++ .../gui/controller/PreferencesController.java | 29 + .../controller/SaveAllSourcesController.java | 94 +++ .../SearchInConstantPoolsController.java | 466 +++++++++++ .../controller/SelectLocationController.java | 181 +++++ .../model/configuration/Configuration.java | 103 +++ .../container/FilteredContainerWrapper.java | 114 +++ .../org/jd/gui/model/history/History.java | 83 ++ .../ContextualActionsFactoryService.java | 85 ++ .../ConfigurationPersister.java} | 6 +- .../ConfigurationPersisterService.java | 22 + .../ConfigurationXmlPersisterProvider.java | 276 +++++++ .../container/ContainerFactoryService.java | 33 + .../service/extension/ExtensionService.java | 85 ++ .../service/fileloader/FileLoaderService.java | 51 ++ .../gui/service/indexer/IndexerService.java | 110 +++ .../ContainerPanelFactoryProvider.java | 194 +++++ .../mainpanel/PanelFactoryService.java | 38 + .../pastehandler/PasteHandlerService.java | 30 + .../gui/service/platform/PlatformService.java | 36 + .../PreferencesPanelService.java | 46 ++ .../UISingleInstancePreferencesProvider.java | 55 ++ .../UITabsPreferencesProvider.java | 54 ++ .../sourcesaver/SourceSaverService.java | 110 +++ .../treenode/TreeNodeFactoryService.java | 110 +++ .../gui/service/type/TypeFactoryService.java | 122 +++ .../service/uriloader/UriLoaderService.java | 44 ++ .../jd/gui/util/exception/ExceptionUtil.java | 16 +- .../org/jd/gui/util/function/TriConsumer.java | 17 + .../net/InterProcessCommunicationUtil.java | 54 ++ .../java/org/jd/gui/util/net/UriUtil.java | 68 ++ .../org/jd/gui/util/swing/SwingUtil.java} | 73 +- .../main/java/org/jd/gui/view/AboutView.java | 146 ++++ .../main/java/org/jd/gui/view/GoToView.java | 164 ++++ .../main/java/org/jd/gui/view/MainView.java | 452 +++++++++++ .../jd/gui/view/OpenTypeHierarchyView.java | 451 +++++++++++ .../java/org/jd/gui/view/OpenTypeView.java | 266 +++++++ .../java/org/jd/gui/view/PreferencesView.java | 193 +++++ .../org/jd/gui/view/SaveAllSourcesView.java | 122 +++ .../gui/view/SearchInConstantPoolsView.java | 430 ++++++++++ .../org/jd/gui/view/SelectLocationView.java | 202 +++++ .../gui/view/bean/OpenTypeListCellBean.java | 35 + .../org/jd/gui/view/component/IconButton.java | 27 + .../java/org/jd/gui/view/component/List.java | 90 +++ .../java/org/jd/gui/view/component/Tree.java | 34 + .../view/component/panel/MainTabbedPanel.java | 197 +++++ .../gui/view/component/panel/TabbedPanel.java | 260 ++++++ .../view/component/panel/TreeTabbedPanel.java | 321 ++++++++ .../renderer/OpenTypeListCellRenderer.java | 84 ++ .../gui/view/renderer/TreeNodeRenderer.java | 84 ++ build.gradle | 24 +- services/build.gradle | 7 +- .../gui/model/container/EarContainer.groovy | 24 - .../model/container/GenericContainer.groovy | 162 ---- .../gui/model/container/JarContainer.groovy | 24 - .../gui/model/container/WarContainer.groovy | 24 - ...alifiedNameContextualActionsFactory.groovy | 97 --- .../EarContainerFactoryProvider.groovy | 39 - .../GenericContainerFactoryProvider.groovy | 26 - .../JarContainerFactoryProvider.groovy | 40 - .../WarContainerFactoryProvider.groovy | 39 - .../AbstractFileLoaderProvider.groovy | 62 -- .../AbstractTypeFileLoaderProvider.groovy | 59 -- .../fileloader/ClassFileLoaderProvider.groovy | 30 - .../fileloader/EarFileLoaderProvider.groovy | 20 - .../fileloader/JarFileLoaderProvider.groovy | 20 - .../fileloader/JavaFileLoaderProvider.groovy | 37 - .../fileloader/LogFileLoaderProvider.groovy | 26 - .../fileloader/WarFileLoaderProvider.groovy | 20 - .../fileloader/ZipFileLoaderProvider.groovy | 45 -- .../indexer/DirectoryIndexerProvider.groovy | 46 -- .../EjbJarXmlFileIndexerProvider.groovy | 70 -- .../MetainfServiceFileIndexerProvider.groovy | 43 - .../indexer/TextFileIndexerProvider.groovy | 29 - .../indexer/WebXmlFileIndexerProvider.groovy | 46 -- .../XmlBasedFileIndexerProvider.groovy | 102 --- .../indexer/XmlFileIndexerProvider.groovy | 120 --- .../indexer/ZipFileIndexerProvider.groovy | 32 - .../pastehandler/LogPasteHandler.groovy | 25 - .../ClassFileSaverPreferencesProvider.groovy | 82 -- .../ClassFileViewerPreferencesProvider.groovy | 70 -- ...DirectoryIndexerPreferencesProvider.groovy | 84 -- .../ViewerPreferencesProvider.groovy | 96 --- .../DirectorySourceSaverProvider.groovy | 63 -- .../FileSourceSaverProvider.groovy | 45 -- .../PackageSourceSaverProvider.groovy | 23 - .../ZipFileSourceSaverProvider.groovy | 54 -- ...ractTypeFileTreeNodeFactoryProvider.groovy | 154 ---- .../ClassFileTreeNodeFactoryProvider.groovy | 87 -- .../CssFileTreeNodeFactoryProvider.groovy | 48 -- .../DirectoryTreeNodeFactoryProvider.groovy | 101 --- .../DtdFileTreeNodeFactoryProvider.groovy | 47 -- .../EarFileTreeNodeFactoryProvider.groovy | 35 - ...jbJarXmlFileTreeNodeFactoryProvider.groovy | 42 - .../FileTreeNodeFactoryProvider.groovy | 47 -- .../HtmlFileTreeNodeFactoryProvider.groovy | 47 -- .../JarFileTreeNodeFactoryProvider.groovy | 50 -- .../JavaFileTreeNodeFactoryProvider.groovy | 51 -- ...vascriptFileTreeNodeFactoryProvider.groovy | 47 -- .../JsonFileTreeNodeFactoryProvider.groovy | 47 -- .../JspFileTreeNodeFactoryProvider.groovy | 48 -- ...ManifestFileTreeNodeFactoryProvider.groovy | 42 - ...infDirectoryTreeNodeFactoryProvider.groovy | 28 - ...fServiceFileTreeNodeFactoryProvider.groovy | 51 -- .../PackageTreeNodeFactoryProvider.groovy | 63 -- ...opertiesFileTreeNodeFactoryProvider.groovy | 47 -- .../SqlFileTreeNodeFactoryProvider.groovy | 47 -- .../TextFileTreeNodeFactoryProvider.groovy | 72 -- .../WarFileTreeNodeFactoryProvider.groovy | 35 - .../WarPackageTreeNodeFactoryProvider.groovy | 23 - .../WebXmlFileTreeNodeFactoryProvider.groovy | 44 -- ...sesDirectoryTreeNodeFactoryProvider.groovy | 22 - ...LibDirectoryTreeNodeFactoryProvider.groovy | 22 - ...XmlBasedFileTreeNodeFactoryProvider.groovy | 47 -- .../XmlFileTreeNodeFactoryProvider.groovy | 43 - .../ZipFileTreeNodeFactoryProvider.groovy | 61 -- .../uriloader/FileUriLoaderProvider.groovy | 23 - .../jd/gui/util/JarContainerEntryUtil.groovy | 95 --- .../view/component/AbstractTextPage.groovy | 402 ---------- .../gui/view/component/ClassFilePage.groovy | 282 ------- .../component/CustomLineNumbersPage.groovy | 329 -------- .../view/component/EjbJarXmlFilePage.groovy | 142 ---- .../gui/view/component/HyperlinkPage.groovy | 115 --- .../jd/gui/view/component/JavaFilePage.groovy | 737 ----------------- .../org/jd/gui/view/component/LogPage.groovy | 132 ---- .../view/component/ManifestFilePage.groovy | 196 ----- .../OneTypeReferenceByLinePage.groovy | 105 --- .../org/jd/gui/view/component/TextPage.groovy | 44 -- .../org/jd/gui/view/component/TypePage.groovy | 474 ----------- .../view/component/TypeReferencePage.groovy | 103 --- .../gui/view/component/WebXmlFilePage.groovy | 173 ---- .../jd/gui/view/component/XmlFilePage.groovy | 110 --- .../java/org/fife/ui/rtextarea/Marker.java | 29 + .../container/ContainerEntryComparator.java | 32 + .../jd/gui/model/container/EarContainer.java | 21 + .../gui/model/container/GenericContainer.java | 179 +++++ .../jd/gui/model/container/JarContainer.java | 21 + .../jd/gui/model/container/WarContainer.java | 21 + ...QualifiedNameContextualActionsFactory.java | 100 +++ .../actions/InvalidFormatException.java | 14 +- .../EarContainerFactoryProvider.java | 41 + .../GenericContainerFactoryProvider.java | 26 + .../JarContainerFactoryProvider.java | 42 + .../WarContainerFactoryProvider.java | 41 + .../AbstractFileLoaderProvider.java | 100 +++ .../AbstractTypeFileLoaderProvider.java | 69 ++ .../fileloader/ClassFileLoaderProvider.java | 37 + .../fileloader/EarFileLoaderProvider.java | 23 + .../fileloader/JarFileLoaderProvider.java | 23 + .../fileloader/JavaFileLoaderProvider.java | 42 + .../fileloader/LogFileLoaderProvider.java | 30 + .../fileloader/WarFileLoaderProvider.java | 23 + .../fileloader/ZipFileLoaderProvider.java | 59 ++ .../indexer/AbstractIndexerProvider.java | 43 +- .../indexer/ClassFileIndexerProvider.java | 42 +- .../indexer/DirectoryIndexerProvider.java | 48 ++ .../indexer/EjbJarXmlFileIndexerProvider.java | 75 ++ .../indexer/JavaFileIndexerProvider.java | 27 +- .../MetainfServiceFileIndexerProvider.java | 49 ++ .../indexer/TextFileIndexerProvider.java | 29 + .../indexer/WebXmlFileIndexerProvider.java | 51 ++ .../indexer/XmlBasedFileIndexerProvider.java | 112 +++ .../indexer/XmlFileIndexerProvider.java | 130 +++ .../indexer/ZipFileIndexerProvider.java | 33 + .../service/pastehandler/LogPasteHandler.java | 27 + .../ClassFileSaverPreferencesProvider.java | 81 ++ .../ClassFileViewerPreferencesProvider.java | 70 ++ .../DirectoryIndexerPreferencesProvider.java | 91 +++ .../ViewerPreferencesProvider.java | 110 +++ .../AbstractSourceSaverProvider.java | 41 +- .../ClassFileSourceSaverProvider.java | 30 +- .../DirectorySourceSaverProvider.java | 70 ++ .../sourcesaver/FileSourceSaverProvider.java | 52 ++ .../PackageSourceSaverProvider.java | 23 + .../ZipFileSourceSaverProvider.java | 58 ++ .../AbstractTreeNodeFactoryProvider.java | 45 +- ...stractTypeFileTreeNodeFactoryProvider.java | 216 +++++ .../ClassFileTreeNodeFactoryProvider.java | 100 +++ .../CssFileTreeNodeFactoryProvider.java | 47 ++ .../DirectoryTreeNodeFactoryProvider.java | 103 +++ .../DtdFileTreeNodeFactoryProvider.java | 45 ++ .../EarFileTreeNodeFactoryProvider.java | 34 + .../EjbJarXmlFileTreeNodeFactoryProvider.java | 42 + .../treenode/FileTreeNodeFactoryProvider.java | 47 ++ .../HtmlFileTreeNodeFactoryProvider.java | 45 ++ .../JarFileTreeNodeFactoryProvider.java | 76 ++ .../JavaFileTreeNodeFactoryProvider.java | 55 ++ ...JavascriptFileTreeNodeFactoryProvider.java | 44 ++ .../JsonFileTreeNodeFactoryProvider.java | 45 ++ .../JspFileTreeNodeFactoryProvider.java | 45 ++ .../ManifestFileTreeNodeFactoryProvider.java | 42 + ...tainfDirectoryTreeNodeFactoryProvider.java | 29 + ...infServiceFileTreeNodeFactoryProvider.java | 55 ++ .../PackageTreeNodeFactoryProvider.java | 64 ++ ...PropertiesFileTreeNodeFactoryProvider.java | 47 ++ .../SqlFileTreeNodeFactoryProvider.java | 45 ++ .../TextFileTreeNodeFactoryProvider.java | 80 ++ .../WarFileTreeNodeFactoryProvider.java | 34 + .../WarPackageTreeNodeFactoryProvider.java | 24 + .../WebXmlFileTreeNodeFactoryProvider.java | 42 + ...assesDirectoryTreeNodeFactoryProvider.java | 18 + ...nfLibDirectoryTreeNodeFactoryProvider.java | 18 + .../XmlBasedFileTreeNodeFactoryProvider.java | 45 ++ .../XmlFileTreeNodeFactoryProvider.java | 43 + .../ZipFileTreeNodeFactoryProvider.java | 60 ++ .../type/AbstractTypeFactoryProvider.java | 35 +- .../type/ClassFileTypeFactoryProvider.java | 305 ++++--- .../type/JavaFileTypeFactoryProvider.java | 31 +- .../uriloader/FileUriLoaderProvider.java | 30 + .../util/container/JarContainerEntryUtil.java | 107 +++ .../decompiler/ClassFileSourcePrinter.java | 3 +- .../gui/util/decompiler/GuiPreferences.java | 3 +- .../jd/gui/util/exception/ExceptionUtil.java | 18 +- .../java/org/jd/gui/util/index/IndexUtil.java | 43 + .../java/org/jd/gui/util/io/TextReader.java | 41 + .../util/parser/antlr/ANTLRJavaParser.java | 5 +- .../parser/antlr/AbstractJavaListener.java | 7 +- .../gui/util/xml/AbstractXmlPathFinder.java | 8 +- .../gui/view/component/AbstractTextPage.java | 427 ++++++++++ .../jd/gui/view/component/ClassFilePage.java | 293 +++++++ .../view/component/CustomLineNumbersPage.java | 315 ++++++++ .../gui/view/component/EjbJarXmlFilePage.java | 161 ++++ .../jd/gui/view/component/HyperlinkPage.java | 115 +++ .../jd/gui/view/component/JavaFilePage.java | 747 ++++++++++++++++++ .../org/jd/gui/view/component/LogPage.java | 144 ++++ .../gui/view/component/ManifestFilePage.java | 214 +++++ .../component/OneTypeReferenceByLinePage.java | 136 ++++ ...rorStrip.java => RoundMarkErrorStrip.java} | 71 +- .../org/jd/gui/view/component/TextPage.java | 46 ++ .../org/jd/gui/view/component/TypePage.java | 526 ++++++++++++ .../gui/view/component/TypeReferencePage.java | 116 +++ .../jd/gui/view/component/WebXmlFilePage.java | 213 +++++ .../jd/gui/view/component/XmlFilePage.java | 132 ++++ .../org/jd/gui/view/data/TreeNodeBean.java | 80 ++ 299 files changed, 15480 insertions(+), 13746 deletions(-) delete mode 100644 app/src/main/groovy/org/jd/gui/App.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/Constants.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/OsxApp.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/GoToController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/MainController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/OpenTypeController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/OpenTypeHierarchyController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/PreferencesController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/SaveAllSourcesController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/SearchInConstantPoolsController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/controller/SelectLocationController.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/model/configuration/Configuration.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/model/container/FilteredContainerWrapper.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/model/history/History.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/actions/ContextualActionsFactoryService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersisterService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/container/ContainerFactoryService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/extension/ExtensionService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/fileloader/FileLoaderService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/indexer/IndexerService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/mainpanel/PanelFactoryService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/pastehandler/PasteHandlerService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/platform/PlatformService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/preferencespanel/PreferencesPanelService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/sourcesaver/SourceSaverService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/treenode/TreeNodeFactoryService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/type/TypeFactoryService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/service/uriloader/UriLoaderService.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/util/net/InterProcessCommunications.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/util/net/UriUtil.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/GoToDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/GoToView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/MainDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/MainView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/OpenTypeDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/OpenTypeView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/PreferencesDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/PreferencesView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/SaveAllSourcesDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/SaveAllSourcesView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsDescription.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/SelectLocationView.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/component/List.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/component/Tree.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/component/panel/MainTabbedPanel.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/component/panel/TabbedPanel.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/component/panel/TreeTabbedPanel.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/renderer/OpenTypeListCellRenderer.groovy delete mode 100644 app/src/main/groovy/org/jd/gui/view/renderer/TreeNodeRenderer.groovy create mode 100644 app/src/main/java/org/jd/gui/App.java create mode 100644 app/src/main/java/org/jd/gui/Constants.java create mode 100644 app/src/main/java/org/jd/gui/OsxApp.java create mode 100644 app/src/main/java/org/jd/gui/controller/AboutController.java create mode 100644 app/src/main/java/org/jd/gui/controller/GoToController.java create mode 100644 app/src/main/java/org/jd/gui/controller/MainController.java create mode 100644 app/src/main/java/org/jd/gui/controller/OpenTypeController.java create mode 100644 app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java create mode 100644 app/src/main/java/org/jd/gui/controller/PreferencesController.java create mode 100644 app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java create mode 100644 app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java create mode 100644 app/src/main/java/org/jd/gui/controller/SelectLocationController.java create mode 100644 app/src/main/java/org/jd/gui/model/configuration/Configuration.java create mode 100644 app/src/main/java/org/jd/gui/model/container/FilteredContainerWrapper.java create mode 100644 app/src/main/java/org/jd/gui/model/history/History.java create mode 100644 app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java rename app/src/main/{groovy/org/jd/gui/service/configuration/ConfigurationPersister.groovy => java/org/jd/gui/service/configuration/ConfigurationPersister.java} (69%) create mode 100644 app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java create mode 100644 app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java create mode 100644 app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java create mode 100644 app/src/main/java/org/jd/gui/service/extension/ExtensionService.java create mode 100644 app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java create mode 100644 app/src/main/java/org/jd/gui/service/indexer/IndexerService.java create mode 100644 app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java create mode 100644 app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java create mode 100644 app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java create mode 100644 app/src/main/java/org/jd/gui/service/platform/PlatformService.java create mode 100644 app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java create mode 100644 app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java create mode 100644 app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java create mode 100644 app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java create mode 100644 app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java create mode 100644 app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java create mode 100644 app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java rename services/src/main/groovy/org/jd/gui/view/data/TreeNodeBean.groovy => app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java (54%) create mode 100644 app/src/main/java/org/jd/gui/util/function/TriConsumer.java create mode 100644 app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java create mode 100644 app/src/main/java/org/jd/gui/util/net/UriUtil.java rename app/src/main/{groovy/org/jd/gui/util/swing/SwingUtil.groovy => java/org/jd/gui/util/swing/SwingUtil.java} (70%) create mode 100644 app/src/main/java/org/jd/gui/view/AboutView.java create mode 100644 app/src/main/java/org/jd/gui/view/GoToView.java create mode 100644 app/src/main/java/org/jd/gui/view/MainView.java create mode 100644 app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java create mode 100644 app/src/main/java/org/jd/gui/view/OpenTypeView.java create mode 100644 app/src/main/java/org/jd/gui/view/PreferencesView.java create mode 100644 app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java create mode 100644 app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java create mode 100644 app/src/main/java/org/jd/gui/view/SelectLocationView.java create mode 100644 app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java create mode 100644 app/src/main/java/org/jd/gui/view/component/IconButton.java create mode 100644 app/src/main/java/org/jd/gui/view/component/List.java create mode 100644 app/src/main/java/org/jd/gui/view/component/Tree.java create mode 100644 app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java create mode 100644 app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java create mode 100644 app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java create mode 100644 app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java create mode 100644 app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java delete mode 100644 services/src/main/groovy/org/jd/gui/model/container/EarContainer.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/model/container/GenericContainer.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/model/container/JarContainer.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/model/container/WarContainer.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/container/EarContainerFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/container/GenericContainerFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/container/JarContainerFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/container/WarContainerFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/EarFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/JarFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/LogFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/WarFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/fileloader/ZipFileLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/DirectoryIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/TextFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/XmlFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/indexer/ZipFileIndexerProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/pastehandler/LogPasteHandler.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/service/uriloader/FileUriLoaderProvider.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/util/JarContainerEntryUtil.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/AbstractTextPage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/ClassFilePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/CustomLineNumbersPage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/EjbJarXmlFilePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/HyperlinkPage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/JavaFilePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/LogPage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/ManifestFilePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/OneTypeReferenceByLinePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/TextPage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/TypePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/TypeReferencePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/WebXmlFilePage.groovy delete mode 100644 services/src/main/groovy/org/jd/gui/view/component/XmlFilePage.groovy create mode 100644 services/src/main/java/org/fife/ui/rtextarea/Marker.java create mode 100644 services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java create mode 100644 services/src/main/java/org/jd/gui/model/container/EarContainer.java create mode 100644 services/src/main/java/org/jd/gui/model/container/GenericContainer.java create mode 100644 services/src/main/java/org/jd/gui/model/container/JarContainer.java create mode 100644 services/src/main/java/org/jd/gui/model/container/WarContainer.java create mode 100644 services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java rename app/src/main/groovy/org/jd/gui/view/bean/OpenTypeListCellBean.groovy => services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java (51%) create mode 100644 services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java create mode 100644 services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java create mode 100644 services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java create mode 100644 services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java rename app/src/main/groovy/org/jd/gui/view/component/IconButton.groovy => services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java (52%) create mode 100644 services/src/main/java/org/jd/gui/util/index/IndexUtil.java create mode 100644 services/src/main/java/org/jd/gui/util/io/TextReader.java create mode 100644 services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/ClassFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/JavaFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/LogPage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/OneTypeReferenceByLinePage.java rename services/src/main/java/org/jd/gui/view/component/{ErrorStrip.java => RoundMarkErrorStrip.java} (96%) create mode 100644 services/src/main/java/org/jd/gui/view/component/TextPage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/TypePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/component/XmlFilePage.java create mode 100644 services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java diff --git a/api/src/main/java/org/jd/gui/spi/PreferencesPanel.java b/api/src/main/java/org/jd/gui/spi/PreferencesPanel.java index fa7c7b0d..33e5bd67 100644 --- a/api/src/main/java/org/jd/gui/spi/PreferencesPanel.java +++ b/api/src/main/java/org/jd/gui/spi/PreferencesPanel.java @@ -7,6 +7,7 @@ package org.jd.gui.spi; +import javax.swing.*; import java.awt.*; import java.util.Map; @@ -15,6 +16,8 @@ public interface PreferencesPanel { String getPreferencesPanelTitle(); + JComponent getPanel(); + void init(Color errorBackgroundColor); boolean isActivated(); diff --git a/app/build.gradle b/app/build.gradle index f852d917..6366e3bd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,10 +1,7 @@ -apply plugin: 'groovy' +apply plugin: 'java' dependencies { - compile 'org.codehaus.groovy:groovy:2.5.6' - compile 'org.codehaus.groovy:groovy-swing:2.5.6' - compile 'org.codehaus.groovy:groovy-xml:2.5.6' - provided 'com.yuvimasory:orange-extensions:1.3.0' + provided 'com.yuvimasory:orange-extensions:1.3.0' // OSX support compile project(':api') runtime project(':services') } diff --git a/app/src/main/groovy/org/jd/gui/App.groovy b/app/src/main/groovy/org/jd/gui/App.groovy deleted file mode 100644 index 06bc07d0..00000000 --- a/app/src/main/groovy/org/jd/gui/App.groovy +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.service.configuration.ConfigurationPersisterService -import org.jd.gui.util.net.InterProcessCommunications - -import javax.swing.JOptionPane - -import org.jd.gui.controller.MainController - -class App { - static final String SINGLE_INSTANCE = 'UIMainWindowPreferencesProvider.singleInstance' - - static MainController controller - - static void main(String[] args) { - if (args.contains("-h")) { - 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 - def persister = ConfigurationPersisterService.instance.get() - def configuration = persister.load() - addShutdownHook { persister.save(configuration) } - - if ('true'.equals(configuration.preferences.get(SINGLE_INSTANCE))) { - def ipc = new InterProcessCommunications() - - try { - ipc.listen { String[] receivedArgs -> - controller.openFiles(receivedArgs.collect { new File(it) }) - } - } catch (Exception notTheFirstInstanceException) { - // Send args to main windows and exit - ipc.send(args) - System.exit(0) - } - } - - // Create SwingBuilder, set look and feel - def swing = new SwingBuilder() - swing.lookAndFeel(configuration.lookAndFeel) - - // Create main controller and show main frame - controller = new MainController(swing, configuration) - controller.show() - controller.openFiles(args.collect { new File(it) }) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/Constants.groovy b/app/src/main/groovy/org/jd/gui/Constants.groovy deleted file mode 100644 index 718c3d12..00000000 --- a/app/src/main/groovy/org/jd/gui/Constants.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 - -class Constants { - final static String APP_NAME = "JD-GUI" - - final static int DEFAULT_WIDTH = 600 - final static int DEFAULT_HEIGHT = 400 - - final static int MINIMAL_WIDTH = 500 - final static int MINIMAL_HEIGHT = 160 - - final static String CONFIG_FILENAME = "jd-gui.cfg" - - final static int MAX_RECENT_FILES = 10 - final static int RECENT_FILE_MAX_LENGTH = 200 -} diff --git a/app/src/main/groovy/org/jd/gui/OsxApp.groovy b/app/src/main/groovy/org/jd/gui/OsxApp.groovy deleted file mode 100644 index a015bcf5..00000000 --- a/app/src/main/groovy/org/jd/gui/OsxApp.groovy +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.AppEvent -import com.apple.eawt.Application -import com.apple.eawt.OpenFilesHandler -import com.apple.eawt.QuitHandler -import com.apple.eawt.QuitResponse - -class OsxApp extends App { - - static void main(String[] args) { - // Create an instance of the mac OSX Application class - def application = new Application() - - App.main(args) - - // Add an handle invoked when the application is asked to open a list of files - application.setOpenFileHandler(new OpenFilesHandler() { - void openFiles(AppEvent.OpenFilesEvent e) { - controller.openFiles(e.files) - } - }) - - // Add an handle invoked when the application is asked to quit - application.setQuitHandler(new QuitHandler() { - void handleQuitRequestWith(AppEvent.QuitEvent e, QuitResponse response) { - System.exit(0) - } - }) - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/GoToController.groovy b/app/src/main/groovy/org/jd/gui/controller/GoToController.groovy deleted file mode 100644 index 45a24142..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/GoToController.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.feature.LineNumberNavigable -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.view.GoToView - -class GoToController { - GoToView goToView - - GoToController(SwingBuilder swing, Configuration configuration) { - // Create UI - goToView = new GoToView(swing, configuration) - } - - void show(LineNumberNavigable navigator, Closure okClosure) { - // Show - goToView.show(navigator, okClosure) - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/MainController.groovy b/app/src/main/groovy/org/jd/gui/controller/MainController.groovy deleted file mode 100644 index 82f201f9..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/MainController.groovy +++ /dev/null @@ -1,565 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.feature.ContentCopyable -import org.jd.gui.api.feature.ContentIndexable -import org.jd.gui.api.feature.ContentSavable -import org.jd.gui.api.feature.ContentSearchable -import org.jd.gui.api.feature.FocusedTypeGettable -import org.jd.gui.api.feature.IndexesChangeListener -import org.jd.gui.api.feature.LineNumberNavigable -import org.jd.gui.api.feature.ContentSelectable -import org.jd.gui.api.feature.PreferencesChangeListener -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.model.history.History -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.actions.ContextualActionsFactoryService -import org.jd.gui.service.platform.PlatformService -import org.jd.gui.service.preferencespanel.PreferencesPanelService -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.ContainerFactory -import org.jd.gui.spi.FileLoader -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.spi.TypeFactory -import org.jd.gui.spi.UriLoader -import org.jd.gui.util.net.UriUtil -import org.jd.gui.util.swing.SwingUtil - -import javax.swing.Action -import javax.swing.Icon -import javax.swing.JComponent -import javax.swing.JFileChooser -import javax.swing.JLayer -import javax.swing.TransferHandler - -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.view.MainView - -import javax.swing.filechooser.FileNameExtensionFilter -import javax.swing.filechooser.FileSystemView -import java.awt.Desktop -import java.awt.EventDispatchThread -import java.awt.Frame -import java.awt.Point -import java.awt.Toolkit -import java.awt.WaitDispatchSupport -import java.awt.datatransfer.DataFlavor -import java.awt.event.ComponentAdapter -import java.awt.event.ComponentEvent -import java.nio.file.Path -import java.util.concurrent.Callable -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors - -class MainController implements API { - SwingBuilder swing - Configuration configuration - MainView mainView - - GoToController goToController - OpenTypeController openTypeController - OpenTypeHierarchyController openTypeHierarchyController - PreferencesController preferencesController - SearchInConstantPoolsController searchInConstantPoolsController - SaveAllSourcesController saveAllSourcesController - SelectLocationController selectLocationController - - History history = new History() - JComponent currentPage = null - ExecutorService executor - Collection containerChangeListeners = [] - - MainController(SwingBuilder swing, Configuration configuration) { - this.swing = swing - this.configuration = configuration - - if (PlatformService.instance.isLinux) { - // Fix for GTKLookAndFeel - SwingUtil.installGtkPopupBugWorkaround() - } - - // Create main frame - mainView = new MainView( - swing, - configuration, - this, - history, - { panelClosed() }, // panelClosedClosure - { page -> onCurrentPageChanged(page) }, // currentPageChangedClosure - { file -> openFiles([file]) }, // openFilesClosure - { onFindCriteriaChanged() } // findCriteriaChangedClosure - ) - } - - // --- Show GUI --- // - void show() { - // Show main frame - mainView.show( - configuration.mainWindowLocation, - configuration.mainWindowSize, - configuration.mainWindowMaximize) - - mainView.updateRecentFilesMenu(configuration.recentFiles) - - swing.doLater { - // Setup closures - openAction.closure = { onOpen() } - - saveAllSourcesController = new SaveAllSourcesController(swing, configuration, this) - saveAllSourcesAction.closure = { onSaveAllSources() } - - containerChangeListeners.add(openTypeController = new OpenTypeController(swing, configuration, this)) - openTypeAction.closure = { onOpenType() } - - containerChangeListeners.add(openTypeHierarchyController = new OpenTypeHierarchyController(swing, configuration, this)) - openTypeHierarchyAction.closure = { onOpenTypeHierarchy() } - - closeAction.closure = { onClose() } - saveAction.closure = { onSaveSource() } - copyAction.closure = { onCopy() } - pasteAction.closure = { onPaste() } - selectAllAction.closure = { onSelectAll() } - - findAction.closure = { onFind() } - findNextAction.closure = { onFindNext() } - findPreviousAction.closure = { onFindPrevious() } - findCaseSensitiveAction.closure = { onFindCriteriaChanged() } - - goToController = new GoToController(swing, configuration) - goToAction.closure = { onGoTo() } - - backwardAction.closure = { openURI(history.backward()) } - forwardAction.closure = { openURI(history.forward()) } - - containerChangeListeners.add(searchInConstantPoolsController = new SearchInConstantPoolsController(swing, configuration, this)) - searchAction.closure = { onSearch() } - - wikipediaAction.closure = { onWikipedia() } - - preferencesController = new PreferencesController(swing, configuration, this, PreferencesPanelService.instance.providers) - preferencesAction.closure = { onPreferences() } - - // Add listeners - mainFrame.addComponentListener(new MainFrameListener(configuration)) - // Set drop files transfer handler - mainFrame.setTransferHandler(new FilesTransferHandler()) - - // Create executor - executor = Executors.newSingleThreadExecutor() - // Background initializations - executor.execute(new Runnable() { - void run() { - // Background controller creation - selectLocationController = new SelectLocationController(swing, configuration, MainController.this) - // Background service initialization - ContextualActionsFactoryService.instance - ContainerFactoryService.instance - FileLoaderService.instance - IndexerService.instance - PasteHandlerService.instance - PreferencesPanelService.instance - TreeNodeFactoryService.instance - TypeFactoryService.instance - UriLoaderService.instance - // Background class loading - new JFileChooser().addChoosableFileFilter(new FileNameExtensionFilter('', 'dummy')) - new WaitDispatchSupport(new EventDispatchThread(null, 'dummy', null), null) - FileSystemView.getFileSystemView().isFileSystemRoot(new File('dummy')) - new JLayer() - new JComponent.KeyboardState() - new JComponent.IntVector() - } - }) - } - } - - // --- Actions --- // - void onOpen() { - mainView.createOpenFileChooser().with { - addFileFilters(FileLoaderService.instance.mapProviders) - currentDirectory = configuration.configRecentLoadDirectory - show({ openFiles([ selectedFile ]) }) - configuration.configRecentLoadDirectory = currentDirectory - } - } - - void onClose() { - mainView.closeCurrentTab() - } - - void onSaveSource() { - if (currentPage instanceof ContentSavable) { - mainView.createSaveFileChooser().with { - selectedFile = new File(configuration.configRecentSaveDirectory, currentPage.fileName) - show({ file -> file.withOutputStream { os -> currentPage.save(this, os) } }) - configuration.configRecentSaveDirectory = currentDirectory - } - } - } - - void onSaveAllSources() { - if (! saveAllSourcesController.isActivated()) { - def currentPanel = mainView.selectedMainPanel - - if (currentPanel instanceof SourcesSavable) { - mainView.createSaveFileChooser().with { - selectedFile = new File(configuration.configRecentSaveDirectory, currentPanel.sourceFileName) - show({ file -> saveAllSourcesController.show(executor, currentPanel, file) }) - configuration.configRecentSaveDirectory = currentDirectory - } - } - } - } - - void onCopy() { - if (currentPage instanceof ContentCopyable) { - currentPage.copy() - } - } - - void onPaste() { - try { - def transferable = Toolkit.defaultToolkit.systemClipboard.getContents(null) - - if (transferable?.isDataFlavorSupported(DataFlavor.stringFlavor)) { - def obj = transferable.getTransferData(DataFlavor.stringFlavor) - PasteHandlerService.instance.get(obj)?.paste(this, obj) - } - } catch (Exception ignore) { - } - } - - void onSelectAll() { - if (currentPage instanceof ContentSelectable) { - currentPage.selectAll() - } - } - - void onFind() { - if (currentPage instanceof ContentSearchable) { - mainView.showFindPanel() - } - } - - void onFindCriteriaChanged() { - if (currentPage instanceof ContentSearchable) { - mainView.setFindBackgroundColor(currentPage.highlightText(mainView.findText, mainView.findCaseSensitive)) - } - } - - void onFindNext() { - if (currentPage instanceof ContentSearchable) { - currentPage.findNext(mainView.findText, mainView.findCaseSensitive) - } - } - - void onOpenType() { - openTypeController.show(collectionOfIndexes, { uri -> openURI(uri) }) - } - - void onOpenTypeHierarchy() { - if (currentPage instanceof FocusedTypeGettable) { - openTypeHierarchyController.show(collectionOfIndexes, currentPage.entry, currentPage.focusedTypeName, { uri -> openURI(uri) }) - } - } - - void onGoTo() { - if (currentPage instanceof LineNumberNavigable) { - goToController.show(currentPage, { int lineNumber -> currentPage.goToLineNumber(lineNumber) }) - } - } - - void onSearch() { - searchInConstantPoolsController.show(collectionOfIndexes, { uri -> openURI(uri) }) - } - - void onFindPrevious() { - if (currentPage instanceof ContentSearchable) { - currentPage.findPrevious(mainView.findText, mainView.findCaseSensitive) - } - } - - void onWikipedia() { - if (Desktop.isDesktopSupported()) { - def desktop = Desktop.desktop - if (desktop.isSupported(Desktop.Action.BROWSE)) { - desktop.browse(URI.create('http://en.wikipedia.org/wiki/Java_Decompiler')) - } - } - } - - void onPreferences() { - preferencesController.show({ - checkPreferencesChange(currentPage) - mainView.preferencesChanged(preferences) - }) - } - - void onCurrentPageChanged(JComponent page) { - currentPage = page - checkIndexesChange(page) - checkPreferencesChange(page) - } - - void checkIndexesChange(JComponent page) { - if (page instanceof IndexesChangeListener) { - def collectionOfIndexes = getCollectionOfIndexes() - def currentHashcode = Integer.valueOf(collectionOfIndexes.hashCode()) - def lastHashcode = page.getClientProperty('collectionOfIndexes-stamp') - - if (!currentHashcode.equals(lastHashcode)) { - page.indexesChanged(collectionOfIndexes) - page.putClientProperty('collectionOfIndexes-stamp', currentHashcode) - } - } - } - - void checkPreferencesChange(JComponent page) { - if (page instanceof PreferencesChangeListener) { - def preferences = configuration.preferences - def currentHashcode = Integer.valueOf(preferences.hashCode()) - def lastHashcode = page.getClientProperty('preferences-stamp') - - if (!currentHashcode.equals(lastHashcode)) { - page.preferencesChanged(preferences) - page.putClientProperty('preferences-stamp', currentHashcode) - } - } - } - - // --- Operations --- // - void openFiles(List files) { - def errors = [] - - for (def file : files) { - // Check input file - if (file.exists()) { - FileLoader loader = getFileLoader(file) - if (! loader?.accept(this, file)) { - errors << "Invalid input fileloader: '$file.absolutePath'" - } - } else { - errors << "File not found: '$file.absolutePath'" - } - } - - if (errors) { - String messages = '' - - errors.eachWithIndex { message, index -> - if (index == 0) { - messages += message - } else if (index < 20) { - messages += "\n$message" - } else if (index == 20) { - messages += '\n...' - } - } - - mainView.showErrorDialog(messages) - } else { - for (def file : files) { - if (openURI(file.toURI())) { - configuration.addRecentFile(file) - mainView.updateRecentFilesMenu(configuration.recentFiles) - } - } - } - } - - // --- Drop files transfer handler --- // - class FilesTransferHandler extends TransferHandler { - boolean canImport(TransferHandler.TransferSupport info) { - return info.isDataFlavorSupported(DataFlavor.javaFileListFlavor) - } - - boolean importData(TransferHandler.TransferSupport info) { - if (info.isDrop() && info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { - try { - openFiles(info.transferable.getTransferData(DataFlavor.javaFileListFlavor)) - return true - } catch (Exception ignore) { - } - } - return false - } - } - - // --- ComponentListener --- // - class MainFrameListener extends ComponentAdapter { - Configuration configuration - - MainFrameListener(Configuration configuration) { - this.configuration = configuration - } - - void componentMoved(ComponentEvent e) { - def f = e.source - - if ((f.extendedState & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { - configuration.mainWindowMaximize = true - } else { - configuration.mainWindowLocation = f.location - configuration.mainWindowMaximize = false - } - } - - void componentResized(ComponentEvent e) { - def f = e.source - - if ((f.extendedState & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { - configuration.mainWindowMaximize = true - } else { - configuration.mainWindowSize = f.size - configuration.mainWindowMaximize = false - } - } - } - - protected void panelClosed() { - swing.doLater { - // Fire 'indexesChanged' event - def collectionOfIndexes = getCollectionOfIndexes() - for (def listener : containerChangeListeners) { - listener.indexesChanged(collectionOfIndexes) - } - if (currentPage instanceof IndexesChangeListener) { - currentPage.indexesChanged(collectionOfIndexes) - } - } - } - - // --- API --- // - boolean openURI(URI uri) { - if (uri) { - boolean success = mainView.openUri(uri) || getUriLoader(uri)?.load(this, uri) - if (success) { - swing.doLater { - addURI(uri) - closeAction.enabled = true - openTypeAction.enabled = true - } - } - return success - } - return false - } - - boolean openURI(int x, int y, Collection entries, String query, String fragment) { - if (entries) { - if (entries.size() == 1) { - // Open the single entry uri - def entry = entries.iterator().next() - return openURI(UriUtil.createURI(this, collectionOfIndexes, entry, query, fragment)) - } else { - // Multiple entries -> Open a "Select location" popup - def collectionOfIndexes = getCollectionOfIndexes() - selectLocationController.show( - new Point(x+(16+2) as int, y+2 as int), - entries, - { entry -> openURI(UriUtil.createURI(this, collectionOfIndexes, entry, query, fragment)) }, // entry selected closure - { }) // popup close closure - return true - } - } - - return false - } - - void addURI(URI uri) { - history.add(uri) - mainView.updateHistoryActions() - } - - public void addPanel(String title, Icon icon, String tip, T component) { - mainView.addMainPanel(title, icon, tip, component) - - if (component instanceof ContentIndexable) { - if (executor == null) { - executor = Executors.newSingleThreadExecutor() - } - - def futureIndexes = executor.submit(new Callable() { - Indexes call() throws Exception { - return component.index(MainController.this) - } - }) - def indexes = new Indexes() { - void waitIndexers() { futureIndexes.get() } - - Map getIndex(String name) { futureIndexes.get().getIndex(name) } - } - - component.putClientProperty('indexes', indexes) - - swing.doLater { - // Fire 'indexesChanged' event - def collectionOfIndexes = getCollectionOfIndexes() - for (def listener : containerChangeListeners) { - listener.indexesChanged(collectionOfIndexes) - } - if (currentPage instanceof IndexesChangeListener) { - currentPage.indexesChanged(collectionOfIndexes) - } - } - } - - checkIndexesChange(currentPage) - } - - @CompileStatic - Collection getContextualActions(Container.Entry entry, String fragment) { ContextualActionsFactoryService.instance.get(this, entry, fragment) } - - @CompileStatic - FileLoader getFileLoader(File file) { FileLoaderService.instance.get(this, file) } - - @CompileStatic - UriLoader getUriLoader(URI uri) { UriLoaderService.instance.get(this, uri) } - - @CompileStatic - PanelFactory getMainPanelFactory(Container container) { PanelFactoryService.instance.get(container) } - - @CompileStatic - ContainerFactory getContainerFactory(Path rootPath) { ContainerFactoryService.instance.get(this, rootPath) } - - @CompileStatic - TreeNodeFactory getTreeNodeFactory(Container.Entry entry) { TreeNodeFactoryService.instance.get(entry) } - - @CompileStatic - TypeFactory getTypeFactory(Container.Entry entry) { TypeFactoryService.instance.get(entry) } - - @CompileStatic - Indexer getIndexer(Container.Entry entry) { IndexerService.instance.get(entry) } - - @CompileStatic - SourceSaver getSourceSaver(Container.Entry entry) { SourceSaverService.instance.get(entry) } - - @CompileStatic - Map getPreferences() { configuration.preferences } - - Collection getCollectionOfIndexes() { - mainView.mainPanels.collect { it.getClientProperty('indexes') }.grep { it != null } - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/OpenTypeController.groovy b/app/src/main/groovy/org/jd/gui/controller/OpenTypeController.groovy deleted file mode 100644 index 00400e2f..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/OpenTypeController.groovy +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import groovy.transform.CompileStatic -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.model.configuration.Configuration -import org.jd.gui.util.net.UriUtil -import org.jd.gui.view.OpenTypeView - -import java.awt.Cursor -import java.awt.Point -import java.util.regex.Pattern - -class OpenTypeController implements IndexesChangeListener { - static final int CACHE_MAX_ENTRIES = 5*20 - - API api - OpenTypeView openTypeView - SelectLocationController selectLocationController - Collection collectionOfIndexes - Closure openClosure - long indexesHashCode = 0L - Map>> cache - - OpenTypeController(SwingBuilder swing, Configuration configuration, API api) { - this.api = api - // Create UI - openTypeView = new OpenTypeView( - swing, configuration, api, - { newPattern -> updateList(newPattern) }, // onPatternChangedClosure - { leftBottom, entries, tn -> onTypeSelected(leftBottom, entries, tn) }, // onTypeSelectedClosure - ) - selectLocationController = new SelectLocationController(swing, configuration, api) - // 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 - } - } - } - - void show(Collection collectionOfIndexes, Closure openClosure) { - // Init attributes - this.collectionOfIndexes = collectionOfIndexes - this.openClosure = openClosure - // Refresh view - long hashCode = collectionOfIndexes.hashCode() - if (hashCode != indexesHashCode) { - // List of indexes has changed -> Refresh result list - updateList(openTypeView.pattern) - indexesHashCode = hashCode - } - // Show - openTypeView.show() - } - - protected void updateList(String pattern) { - int patternLength = pattern.length() - Map> result - - if (patternLength == 0) { - result = Collections.emptyMap() - } else { - def regExpPattern = createRegExpPattern(pattern) - - result = new HashMap>().withDefault { new HashSet() } - - for (def indexes : collectionOfIndexes) { - String key = String.valueOf(indexes.hashCode()) + '***' + pattern - def matchingEntries = cache.get(key) - - if (matchingEntries) { - // Merge 'result' and 'matchingEntries' - for (def mapEntry : matchingEntries.entrySet()) { - result.get(mapEntry.key).addAll(mapEntry.value) - } - } else { - // Waiting the end of indexation... - openTypeView.swing.openTypeDialog.rootPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)) - def index = indexes.getIndex('typeDeclarations') - openTypeView.swing.openTypeDialog.rootPane.setCursor(Cursor.getDefaultCursor()) - - if (index) { - matchingEntries = new HashMap>().withDefault { new HashSet() } - - // Filter - if (patternLength == 1) { - match(pattern.charAt(0), index, matchingEntries) - } else { - def lastKey = key.substring(0, patternLength-1) - def lastResult = cache.get(lastKey) - if (lastResult) { - match(regExpPattern, lastResult, matchingEntries) - } else { - match(regExpPattern, index, matchingEntries) - } - } - - // Store 'matchingEntries' - cache.put(key, matchingEntries) - // Merge 'result' and 'matchingEntries' - for (def mapEntry : matchingEntries.entrySet()) { - result.get(mapEntry.key).addAll(mapEntry.value) - } - } - } - } - } - - // Display - openTypeView.updateList(result) - } - - @CompileStatic - protected static void match(char c, Map> index, Map> result) { - // Filter - if (Character.isLowerCase(c)) { - char upperCase = Character.toUpperCase(c) - - for (def mapEntry : index.entrySet()) { - def typeName = mapEntry.key - def entries = mapEntry.value - // 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)) { - result[typeName].addAll(entries) - } - } - } - } else { - for (def mapEntry : index.entrySet()) { - def typeName = mapEntry.key - def entries = mapEntry.value - // 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)) { - result[typeName].addAll(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 - */ - @CompileStatic - protected static Pattern createRegExpPattern(String pattern) { - // Create regular expression - int patternLength = pattern.length() - def sbPattern = new StringBuffer(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()) - } - - @CompileStatic - protected static void match(Pattern regExpPattern, Map> index, Map> result) { - for (def mapEntry : index.entrySet()) { - def typeName = mapEntry.key - def entries = mapEntry.value - // 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()) { - result[typeName].addAll(entries) - } - } - } - - protected void onTypeSelected(Point leftBottom, Collection entries, String typeName) { - if (entries.size() == 1) { - // Open the single entry uri - openClosure(UriUtil.createURI(api, collectionOfIndexes, entries.iterator().next(), null, typeName)) - } else { - // Multiple entries -> Open a "Select location" popup - selectLocationController.show( - new Point(leftBottom.x+(16+2) as int, leftBottom.y+2 as int), - entries, - { entry -> openClosure(UriUtil.createURI(api, collectionOfIndexes, entry, null, typeName)) }, // entry selected closure - { openTypeView.focus() }) // popup close closure - } - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - if (openTypeView.isVisible()) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // And refresh - updateList(openTypeView.pattern) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/OpenTypeHierarchyController.groovy b/app/src/main/groovy/org/jd/gui/controller/OpenTypeHierarchyController.groovy deleted file mode 100644 index 43425bc6..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/OpenTypeHierarchyController.groovy +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -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.model.configuration.Configuration -import org.jd.gui.util.net.UriUtil -import org.jd.gui.view.OpenTypeHierarchyView - -import java.awt.Cursor -import java.awt.Point - -class OpenTypeHierarchyController implements IndexesChangeListener { - API api - OpenTypeHierarchyView openTypeHierarchyView - SelectLocationController selectLocationController - Collection collectionOfIndexes - Closure openClosure - - OpenTypeHierarchyController(SwingBuilder swing, Configuration configuration, API api) { - this.api = api - // Create UI - openTypeHierarchyView = new OpenTypeHierarchyView( - swing, configuration, api, - { leftBottom, entries, tn -> onTypeSelected(leftBottom, entries, tn) }, // onTypeSelectedClosure - { parentTypeName -> getSubTypeNames(parentTypeName) }, // getSubTypeNamesClosure - { childTypeName -> getEntries(childTypeName) } // getEntriesClosure - ) - selectLocationController = new SelectLocationController(swing, configuration, api) - } - - void show(Collection collectionOfIndexes, Container.Entry entry, String typeName, Closure openClosure) { - // Init attributes - this.collectionOfIndexes = collectionOfIndexes - this.openClosure = openClosure - // Waiting the end of indexation... - openTypeHierarchyView.swing.mainFrame.rootPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)) - for (def indexes : collectionOfIndexes) { indexes.waitIndexers() } - openTypeHierarchyView.swing.mainFrame.rootPane.setCursor(Cursor.getDefaultCursor()) - // Prepare view - openTypeHierarchyView.updateTree(entry, typeName) - // Show - openTypeHierarchyView.show() - } - - protected List getSubTypeNames(String typeName) { - return collectionOfIndexes.collect{ it.getIndex('subTypeNames').get(typeName) }.grep{ it!=null }.flatten() - } - - protected List getEntries(String typeName) { - return collectionOfIndexes.collect{ it.getIndex('typeDeclarations').get(typeName) }.grep{ it!=null }.flatten() - } - - protected void onTypeSelected(Point leftBottom, Collection entries, String typeName) { - if (entries.size() == 1) { - // Open the single entry uri - openClosure(UriUtil.createURI(api, collectionOfIndexes, entries.iterator().next(), null, typeName)) - } else { - // Multiple entries -> Open a "Select location" popup - selectLocationController.show( - new Point(leftBottom.x+(16+2) as int, leftBottom.y+2 as int), - entries, - { entry -> openClosure(UriUtil.createURI(api, collectionOfIndexes, entry, null, typeName)) }, // entry selected - { openTypeHierarchyView.focus() }) // popup closeClosure - } - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - if (openTypeHierarchyView.isVisible()) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // And refresh - openTypeHierarchyView.updateTree(openTypeHierarchyView.entry, openTypeHierarchyView.typeName) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/PreferencesController.groovy b/app/src/main/groovy/org/jd/gui/controller/PreferencesController.groovy deleted file mode 100644 index 9d2de38d..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/PreferencesController.groovy +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.spi.PreferencesPanel -import org.jd.gui.view.PreferencesView - -class PreferencesController { - PreferencesView preferencesView - - PreferencesController(SwingBuilder swing, Configuration configuration, API api, Collection panels) { - // Create UI - preferencesView = new PreferencesView(swing, configuration, panels) - } - - void show(Closure okClosure) { - // Show - preferencesView.show(okClosure) - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/SaveAllSourcesController.groovy b/app/src/main/groovy/org/jd/gui/controller/SaveAllSourcesController.groovy deleted file mode 100644 index b948e04d..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/SaveAllSourcesController.groovy +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.feature.SourcesSavable -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.view.SaveAllSourcesView - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.util.concurrent.ExecutorService - -class SaveAllSourcesController implements SourcesSavable.Controller, SourcesSavable.Listener { - API api - SaveAllSourcesView saveAllSourcesView - boolean cancel - int counter - int mask - - SaveAllSourcesController(SwingBuilder swing, Configuration configuration, API api) { - this.api = api - // Create UI - this.saveAllSourcesView = new SaveAllSourcesView(swing, configuration, api, { onCanceled() }) - } - - @CompileStatic - void show(ExecutorService executor, SourcesSavable savable, File file) { - // Show - this.saveAllSourcesView.show(file) - // Execute background task - executor.execute(new Runnable() { - void run() { - int fileCount = savable.fileCount - - saveAllSourcesView.updateProgressBar(0) - saveAllSourcesView.setMaxValue(fileCount) - - cancel = false - counter = 0 - mask = 2 - - while (fileCount > 64) { - fileCount >>= 1 - mask <<= 1 - } - - mask-- - - def path = Paths.get(file.toURI()) - Files.deleteIfExists(path) - - try { - savable.save(api, this as SourcesSavable.Controller, this as SourcesSavable.Listener, path) - } catch (Exception e) { - saveAllSourcesView.showActionFailedDialog() - cancel = true - } - - if (cancel) { - Files.deleteIfExists(path) - } - - saveAllSourcesView.hide() - } - }) - } - - boolean isActivated() { saveAllSourcesView.isVisible() } - - protected void onCanceled() { cancel = true } - - // --- SourcesSavable.Controller --- // - boolean isCancelled() { cancel } - - // --- SourcesSavable.Listener --- // - void pathSaved(Path path) { - if (((counter++) & mask) == 0) { - saveAllSourcesView.updateProgressBar(counter) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/SearchInConstantPoolsController.groovy b/app/src/main/groovy/org/jd/gui/controller/SearchInConstantPoolsController.groovy deleted file mode 100644 index 602d6295..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/SearchInConstantPoolsController.groovy +++ /dev/null @@ -1,437 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import groovy.transform.CompileStatic -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.model.configuration.Configuration -import org.jd.gui.model.container.FilteredContainerWrapper -import org.jd.gui.service.type.TypeFactoryService -import org.jd.gui.view.SearchInConstantPoolsView - -import java.awt.Cursor -import java.util.regex.Pattern - -class SearchInConstantPoolsController implements IndexesChangeListener { - static final int CACHE_MAX_ENTRIES = 5*20*9 - - API api - SearchInConstantPoolsView searchInConstantPoolsView - Map>> cache - Set filteredContainerWrappers - Collection collectionOfIndexes - Closure openClosure - long indexesHashCode = 0L - - SearchInConstantPoolsController(SwingBuilder swing, Configuration configuration, API api) { - this.api = api - // Create UI - this.searchInConstantPoolsView = new SearchInConstantPoolsView( - swing, configuration, api, - { pattern, flags -> updateTree(pattern, flags) }, // onPatternChangedClosure - { uri, pattern, flags -> onTypeSelected(uri, pattern, flags) } // onTypeSelectedClosure - ) - // 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 - } - } - this.filteredContainerWrappers = new HashSet() - } - - void show(Collection collectionOfIndexes, Closure openClosure) { - // Init attributes - this.collectionOfIndexes = collectionOfIndexes - this.openClosure = openClosure - // Refresh view - long hashCode = collectionOfIndexes.hashCode() - if (hashCode != indexesHashCode) { - // List of indexes has changed - updateTree(searchInConstantPoolsView.pattern, searchInConstantPoolsView.flags) - indexesHashCode = hashCode - } - // Show - searchInConstantPoolsView.show() - } - - void updateTree(String pattern, int flags) { - filteredContainerWrappers.clear() - - int matchingTypeCount = 0 - int patternLength = pattern.length() - - if (patternLength > 0) { - for (def indexes : collectionOfIndexes) { - def matchingEntries = new HashSet() - - // Waiting the end of indexation... - searchInConstantPoolsView.swing.searchInConstantPoolsDialog.rootPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)) - indexes.waitIndexers() - searchInConstantPoolsView.swing.searchInConstantPoolsDialog.rootPane.setCursor(Cursor.getDefaultCursor()) - // Find matched entries - filter(indexes, pattern, flags, matchingEntries) - - if (! matchingEntries.isEmpty()) { - // Search root container with first matching entry - def parentEntry = matchingEntries.iterator().next() - def container = null - - while (parentEntry.container.root) { - container = parentEntry.container - parentEntry = container.root.parent - } - - // TODO In a future release, display matching strings, types, inner-types, fields and methods, not only matching files - matchingEntries = getOuterEntries(matchingEntries) - - matchingTypeCount += matchingEntries.size() - - // Dummy parent entry wrapper - def parentEntryWrapper = new Container.Entry() { - Collection children - - Container getContainer() { parentEntry.container } - Container.Entry getParent() { null } - URI getUri() { parentEntry.uri } - String getPath() { parentEntry.path } - boolean isDirectory() { parentEntry.isDirectory() } - long length() { parentEntry.length() } - InputStream getInputStream() { parentEntry.inputStream } - Collection getChildren() { children } - } - // Create a filtered container - def containerWrapper = new FilteredContainerWrapper(container, parentEntryWrapper, matchingEntries) - // Initialization of 'children' of dummy parent entry wrapper - parentEntryWrapper.children = containerWrapper.root.children - - filteredContainerWrappers.add(containerWrapper) - } - } - } - - searchInConstantPoolsView.updateTree(filteredContainerWrappers, matchingTypeCount) - } - - @CompileStatic - Collection getOuterEntries(Set matchingEntries) { - def innerTypeEntryToOuterTypeEntry = new HashMap() - def matchingOuterEntriesSet = new HashSet() - - for (def entry : matchingEntries) { - def type = TypeFactoryService.instance.get(entry)?.make(api, entry, null) - - if (type?.outerName) { - def outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry) - - if (outerTypeEntry == null) { - def typeNameToEntry = new HashMap() - def innerTypeNameToOuterTypeName = new HashMap() - - // Populate "typeNameToEntry" and "innerTypeNameToOuterTypeName" - for (def e : entry.parent.children) { - type = TypeFactoryService.instance.get(e)?.make(api, e, null) - - if (type) { - typeNameToEntry.put(type.name, e) - if (type.outerName) { - innerTypeNameToOuterTypeName.put(type.name, type.outerName) - } - } - } - - // Search outer type entries and populate "innerTypeEntryToOuterTypeEntry" - for (def e : innerTypeNameToOuterTypeName.entrySet()) { - def innerTypeEntry = typeNameToEntry.get(e.key) - - if (innerTypeEntry) { - def outerTypeName = e.value - - for (;;) { - def typeName = innerTypeNameToOuterTypeName.get(outerTypeName) - if (typeName) { - outerTypeName = typeName - } else { - break - } - } - - outerTypeEntry = typeNameToEntry.get(outerTypeName) - - if (outerTypeEntry) { - innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry) - } - } - } - - // Get outer type entry - outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry) ?: entry - } - - matchingOuterEntriesSet.add(outerTypeEntry) - } else { - matchingOuterEntriesSet.add(entry) - } - } - - // Return outer type entries sorted by path - def array = matchingOuterEntriesSet.toArray(new Container.Entry[0]) - Arrays.sort(array, new ContainerEntryPathComparator()) - return Arrays.asList(array) - } - - void filter(Indexes indexes, String pattern, int flags, Set matchingEntries) { - boolean declarations = ((flags & SearchInConstantPoolsView.SEARCH_TYPE_DECLARATION) != 0) - boolean references = ((flags & SearchInConstantPoolsView.SEARCH_TYPE_REFERENCE) != 0) - - def matchTypeEntriesWithCharClosure = { c, index -> matchTypeEntriesWithChar(c, index) } - def matchTypeEntriesWithStringClosure = { s, index -> matchTypeEntriesWithString(s, index) } - def matchWithCharClosure = { c, index -> matchWithChar(c, index) } - def matchWithStringClosure = { s, index -> matchWithString(s, index) } - def matchStringWithCharClosure = { c, index -> matchStringWithChar(c, index) } - def matchStringWithStringClosure = { s, index -> matchStringWithString(s, index) } - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_TYPE) != 0) { - if (declarations) - match(indexes, 'typeDeclarations', pattern, - matchTypeEntriesWithCharClosure, matchTypeEntriesWithStringClosure, matchingEntries) - if (references) - match(indexes, 'typeReferences', pattern, - matchTypeEntriesWithCharClosure, matchTypeEntriesWithStringClosure, matchingEntries) - } - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_CONSTRUCTOR) != 0) { - if (declarations) - match(indexes, 'constructorDeclarations', pattern, - matchTypeEntriesWithCharClosure, matchTypeEntriesWithStringClosure, matchingEntries) - if (references) - match(indexes, 'constructorReferences', pattern, - matchTypeEntriesWithCharClosure, matchTypeEntriesWithStringClosure, matchingEntries) - } - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_METHOD) != 0) { - if (declarations) - match(indexes, 'methodDeclarations', pattern, - matchWithCharClosure, matchWithStringClosure, matchingEntries) - if (references) - match(indexes, 'methodReferences', pattern, - matchWithCharClosure, matchWithStringClosure, matchingEntries) - } - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_FIELD) != 0) { - if (declarations) - match(indexes, 'fieldDeclarations', pattern, - matchWithCharClosure, matchWithStringClosure, matchingEntries) - if (references) - match(indexes, 'fieldReferences', pattern, - matchWithCharClosure, matchWithStringClosure, matchingEntries) - } - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_STRING) != 0) { - if (declarations || references) - match(indexes, 'strings', pattern, - matchStringWithCharClosure, matchStringWithStringClosure, matchingEntries) - } - } - - @CompileStatic - void match(Indexes indexes, String indexName, String pattern, - Closure matchWithCharClosure, Closure matchWithStringClosure, Set matchingEntries) { - int patternLength = pattern.length() - - if (patternLength > 0) { - String key = String.valueOf(indexes.hashCode()) + '***' + indexName + '***' + pattern - Map> matchedTypes = cache.get(key) - - if (matchedTypes == null) { - def index = indexes.getIndex(indexName) - - if (patternLength == 1) { - matchedTypes = (Map>)matchWithCharClosure(pattern.charAt(0), index) - } else { - def lastKey = key.substring(0, key.length() - 1) - def lastMatchedTypes = cache.get(lastKey) - if (lastMatchedTypes) { - matchedTypes = (Map>)matchWithStringClosure(pattern, lastMatchedTypes) - } else { - matchedTypes = (Map>)matchWithStringClosure(pattern, index) - } - } - - // Cache matchingEntries - cache.put(key, matchedTypes) - } - - if (matchedTypes) { - for (def entries : matchedTypes.values()) { - matchingEntries.addAll(entries) - } - } - } - } - - @CompileStatic - static Map> matchTypeEntriesWithChar(char c, Map> index) { - if ((c == '*') || (c == '?')) { - return Collections.emptyMap() - } else { - return index.findAll { String typeName, entries -> - // Search last package separator - int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 - int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 - int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) - return (lastIndex < typeName.length()) && (typeName.charAt(lastIndex) == c) - } - } - } - - @CompileStatic - static Map> matchTypeEntriesWithString(String pattern, Map> index) { - def p = createPattern(pattern) - return index.findAll { String typeName, entries -> - // Search last package separator - int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 - int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 - int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) - return p.matcher(typeName.substring(lastIndex)).matches() - } - } - - @CompileStatic - static Map> matchWithChar(char c, Map> index) { - if ((c == '*') || (c == '?')) { - return Collections.emptyMap() - } else { - return index.findAll { String key, entries -> !key.isEmpty() && (key.charAt(0) == c) } - } - } - - @CompileStatic - static Map> matchWithString(String pattern, Map> index) { - def p = createPattern(pattern) - return index.findAll { String key, entries -> p.matcher(key).matches() } - } - - @CompileStatic - static Map> matchStringWithChar(char c, Map> index) { - if ((c == '*') || (c == '?')) { - return Collections.emptyMap() - } else { - def p = Pattern.compile(String.valueOf(c)) - return index.findAll { String key, entries -> p.matcher(key).find() } - } - } - - @CompileStatic - static Map> matchStringWithString(String pattern, Map> index) { - def p = createPattern(pattern) - return index.findAll { String key, entries -> p.matcher(key).find() } - } - - /** - * Create a simple regular expression - * - * Rules: - * '*' matchTypeEntries 0 ou N characters - * '?' matchTypeEntries 1 character - */ - @CompileStatic - static Pattern createPattern(String pattern) { - int patternLength = pattern.length() - def sbPattern = new StringBuffer(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()) - } - - void onTypeSelected(URI uri, String pattern, int flags) { - // Open the single entry uri - def entry = null - - for (def container : filteredContainerWrappers) { - entry = container.getEntry(uri) - if (entry) - break - } - - if (entry) { - def sbPattern = new StringBuffer(200 + pattern.length()) - - sbPattern.append('highlightPattern=') - sbPattern.append(pattern) - sbPattern.append('&highlightFlags=') - - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_DECLARATION) != 0) - sbPattern.append('d') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_REFERENCE) != 0) - sbPattern.append('r') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_TYPE) != 0) - sbPattern.append('t') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_CONSTRUCTOR) != 0) - sbPattern.append('c') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_METHOD) != 0) - sbPattern.append('m') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_FIELD) != 0) - sbPattern.append('f') - if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_STRING) != 0) - sbPattern.append('s') - - // 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(collectionOfIndexes, entry, type) - // - // openClosure(new URI(entry.uri.scheme, entry.uri.host, outerPath, query, null)) - // } else { - def query = sbPattern.toString() - - openClosure(new URI(entry.uri.scheme, entry.uri.host, entry.uri.path, query, null)) - // } - } - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - if (searchInConstantPoolsView.isVisible()) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // And refresh - updateTree(searchInConstantPoolsView.pattern, searchInConstantPoolsView.flags) - } - } - - @CompileStatic - static class ContainerEntryPathComparator implements Comparator { - int compare(Container.Entry e1, Container.Entry e2) { e1.path.compareTo(e2.path) } - boolean equals(Object other) { this == other } - } -} diff --git a/app/src/main/groovy/org/jd/gui/controller/SelectLocationController.groovy b/app/src/main/groovy/org/jd/gui/controller/SelectLocationController.groovy deleted file mode 100644 index d65769b3..00000000 --- a/app/src/main/groovy/org/jd/gui/controller/SelectLocationController.groovy +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.model.container.FilteredContainerWrapper -import org.jd.gui.service.type.TypeFactoryService -import org.jd.gui.view.SelectLocationView - -import java.awt.Point - -class SelectLocationController { - - API api - SelectLocationView selectLocationView - - SelectLocationController(SwingBuilder swing, Configuration configuration, API api) { - this.api = api - // Create UI - selectLocationView = new SelectLocationView(swing, configuration, api) - } - - void show(Point location, Collection entries, Closure locationSelectedClosure, Closure closeClosure) { - // Show UI - def map = [:].withDefault { [] } - - for (def entry : entries) { - def container = entry.container - - // Search root container - while (true) { - def parentContainer = container.root.parent.container - if (parentContainer.root == null) { - break - } else { - container = parentContainer - } - } - - map[container].add(entry) - } - - def filteredContainerWrappers = new HashSet() - - for (def mapEntry : map.entrySet()) { - def container = mapEntry.key - def parentEntry = container.root.parent - - // Dummy parent entry wrapper - def parentEntryWrapper = new Container.Entry() { - Collection children - - Container getContainer() { parentEntry.container } - Container.Entry getParent() { null } - URI getUri() { parentEntry.uri } - String getPath() { parentEntry.path } - boolean isDirectory() { parentEntry.isDirectory() } - long length() { parentEntry.length() } - InputStream getInputStream() { parentEntry.inputStream } - Collection getChildren() { children } - } - // Create a filtered container - // TODO In a future release, display matching types and inner-types, not only matching files - def outerEntries = getOuterEntries(mapEntry.value) - - def containerWrapper = new FilteredContainerWrapper(container, parentEntryWrapper, outerEntries) - // Initialization of 'children' of dummy parent entry wrapper - parentEntryWrapper.children = containerWrapper.root.children - - filteredContainerWrappers.add(containerWrapper) - } - - selectLocationView.show( - location, filteredContainerWrappers, entries.size(), - { uri -> onLocationSelected(filteredContainerWrappers, uri, locationSelectedClosure) }, - closeClosure) - } - - Collection getOuterEntries(Collection entries) { - def innerTypeEntryToOuterTypeEntry = [:] - def outerEntriesSet = new HashSet() - - for (def entry : entries) { - def type = TypeFactoryService.instance.get(entry)?.make(api, entry, null) - - if (type?.outerName) { - def outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry) - - if (outerTypeEntry == null) { - def typeNameToEntry = [:] - def innerTypeNameToOuterTypeName = [:] - - // Populate "typeNameToEntry" and "innerTypeNameToOuterTypeName" - for (def e : entry.parent.children) { - type = TypeFactoryService.instance.get(e)?.make(api, e, null) - - if (type) { - typeNameToEntry.put(type.name, e) - if (type.outerName) { - innerTypeNameToOuterTypeName.put(type.name, type.outerName) - } - } - } - - // Search outer type entries and populate "innerTypeEntryToOuterTypeEntry" - for (def e : innerTypeNameToOuterTypeName.entrySet()) { - def innerTypeEntry = typeNameToEntry.get(e.key) - - if (innerTypeEntry) { - def outerTypeName = e.value - - for (;;) { - def typeName = innerTypeNameToOuterTypeName.get(outerTypeName) - if (typeName) { - outerTypeName = typeName - } else { - break - } - } - - outerTypeEntry = typeNameToEntry.get(outerTypeName) - - if (outerTypeEntry) { - innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry) - } - } - } - - // Get outer type entry - outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry) ?: entry - } - - outerEntriesSet.add(outerTypeEntry) - } else { - outerEntriesSet.add(entry) - } - } - - // Return outer type entries sorted by path - return outerEntriesSet.sort { e1, e2 -> e1.path.compareTo(e2.path) } - } - - void onLocationSelected(Set filteredContainerWrappers, URI uri, Closure locationSelectedClosure) { - // Open the single entry uri - def entry = null - - for (def container : filteredContainerWrappers) { - entry = container.getEntry(uri) - if (entry) { - break - } - } - - if (entry) { - locationSelectedClosure(entry) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/model/configuration/Configuration.groovy b/app/src/main/groovy/org/jd/gui/model/configuration/Configuration.groovy deleted file mode 100644 index 3d785aa1..00000000 --- a/app/src/main/groovy/org/jd/gui/model/configuration/Configuration.groovy +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 java.awt.Dimension -import java.awt.Point - -class Configuration { - Point mainWindowLocation - Dimension mainWindowSize - boolean mainWindowMaximize - String lookAndFeel - - List recentFiles = [] - - File configRecentLoadDirectory - File configRecentSaveDirectory - - Map preferences = [:] - - void addRecentFile(File file) { - recentFiles.remove(file) - recentFiles.add(0, file) - if (recentFiles.size() > 10) { - recentFiles.remove(10) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/model/container/FilteredContainerWrapper.groovy b/app/src/main/groovy/org/jd/gui/model/container/FilteredContainerWrapper.groovy deleted file mode 100644 index b040f041..00000000 --- a/app/src/main/groovy/org/jd/gui/model/container/FilteredContainerWrapper.groovy +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 - -class FilteredContainerWrapper implements Container { - Container container - Map uriToEntry - Map uriToWrapper - Container.Entry root - - FilteredContainerWrapper(Container container, Container.Entry parentEntry, Collection entries) { - this.container = container - this.uriToEntry = new HashMap<>() - this.uriToWrapper = new HashMap<>() - this.root = new EntryWrapper(container.root, parentEntry) - - for (def entry : entries) { - while (entry && !uriToEntry.containsKey(entry.uri)) { - uriToEntry.put(entry.uri, entry) - entry = entry.parent - } - } - } - - protected FilteredContainerWrapper( - Container container, FilteredContainerWrapper.EntryWrapper root, - Map uriToEntry, - Map uriToWrapper) { - this.container = container - this.uriToEntry = uriToEntry - this.uriToWrapper = uriToWrapper - this.root = root - } - - String getType() { container.type } - Container.Entry getRoot() { root } - Container.Entry getEntry(URI uri) { uriToEntry.get(uri) } - Set getUris() { uriToEntry.keySet() } - - class EntryWrapper implements Container.Entry, Comparable { - Container.Entry entry - Container.Entry parent - Collection children - - EntryWrapper(Container.Entry entry, Container.Entry parent) { - this.entry = entry - this.parent = parent - this.children = null - } - - Container getContainer() { - if (entry.container == FilteredContainerWrapper.this.container) { - return FilteredContainerWrapper.this - } else { - def container = entry.container - def root = container.root - def wrapper = uriToWrapper.get(root.uri) - - if (wrapper == null) { - // Search EntryWrapper root - def entryWrapperRoot = this.parent - while (entryWrapperRoot.entry.container == container) { - entryWrapperRoot = entryWrapperRoot.parent - } - - // Create a sub wrapper container - wrapper = new FilteredContainerWrapper(container, entryWrapperRoot, uriToEntry, uriToWrapper) - uriToWrapper.put(root.uri, wrapper) - } - - return wrapper - } - } - - Container.Entry getParent() { parent } - URI getUri() { entry.uri } - String getPath() { entry.path } - boolean isDirectory() { entry.isDirectory() } - long length() { entry.length() } - InputStream getInputStream() { entry.inputStream } - - Collection getChildren() { - if (children == null) { - children = entry.children.grep { uriToEntry.containsKey(it.uri) }.collect { new EntryWrapper(it, this) } - } - return children - } - - int compareTo(EntryWrapper other) { - if (entry.isDirectory()) { - if (!other.isDirectory()) { - return -1 - } - } else { - if (other.isDirectory()) { - return 1 - } - } - return entry.path.compareTo(other.path) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/model/history/History.groovy b/app/src/main/groovy/org/jd/gui/model/history/History.groovy deleted file mode 100644 index 8878527a..00000000 --- a/app/src/main/groovy/org/jd/gui/model/history/History.groovy +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 - -class History { - URI current = null - List backward = [] - List forward = [] - - 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.path.toString().equals(current.path.toString())) { - if ((uri.fragment == null) && (uri.query == null)) { - // Ignore - } else if ((current.fragment == null) && (current.query == 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 - } - - URI backward() { - if (! backward.isEmpty()) { - forward.add(current) - int size = backward.size() - current = backward.remove(size-1) - } - return current - } - - URI forward() { - if (! forward.isEmpty()) { - backward.add(current) - int size = forward.size() - current = forward.remove(size-1) - } - return current - } - - boolean canBackward() { !backward.isEmpty() } - boolean canForward() { !forward.isEmpty() } -} diff --git a/app/src/main/groovy/org/jd/gui/service/actions/ContextualActionsFactoryService.groovy b/app/src/main/groovy/org/jd/gui/service/actions/ContextualActionsFactoryService.groovy deleted file mode 100644 index 996dd0e9..00000000 --- a/app/src/main/groovy/org/jd/gui/service/actions/ContextualActionsFactoryService.groovy +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.Action - -@Singleton(lazy = true) -class ContextualActionsFactoryService { - protected static final ActionNameComparator COMPARATOR = new ActionNameComparator() - - protected final Collection providers = ExtensionService.instance.load(ContextualActionsFactory) - - @CompileStatic - Collection get(API api, Container.Entry entry, String fragment) { - Map> mapActions = [:].withDefault { [] } - - for (def provider : providers) { - def actions = provider.make(api, entry, fragment) - - for (def action : actions) { - mapActions.get(action.getValue(ContextualActionsFactory.GROUP_NAME)).add(action) - } - } - - if (mapActions) { - def result = new ArrayList() - - // Sort by group names - for (def groupName : mapActions.keySet().sort()) { - if (! result.isEmpty()) { - // Add 'null' to mark a separator - result.add(null) - } - - // Sort by names - def actions = mapActions.get(groupName) - Collections.sort(actions, COMPARATOR) - result.addAll(actions) - } - - return result - } else { - return Collections.emptyList() - } - } - - static class ActionNameComparator implements Comparator { - - int compare(Action a1, Action a2) { - String n1 = a1.getValue(Action.NAME) ?: '' - String n2 = a2.getValue(Action.NAME) ?: '' - return n1.compareTo(n2) - } - - boolean equals(Object other) { this == other } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersisterService.groovy b/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersisterService.groovy deleted file mode 100644 index 9a3b1c5b..00000000 --- a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersisterService.groovy +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 - -@Singleton -class ConfigurationPersisterService { - private ConfigurationPersister provider = new ConfigurationXmlPersisterProvider() - - ConfigurationPersister get() { - return provider - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.groovy b/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.groovy deleted file mode 100644 index aebcc20d..00000000 --- a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.groovy +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 groovy.xml.MarkupBuilder -import org.jd.gui.Constants -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.service.platform.PlatformService - -import java.awt.* - -class ConfigurationXmlPersisterProvider implements ConfigurationPersister { - static final String ERROR_BACKGROUND_COLOR = 'JdGuiPreferences.errorBackgroundColor' - - static final File FILE = getConfigFile() - - static File getConfigFile() { - if (PlatformService.instance.isLinux) { - // See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - def xdgConfigHome = System.getenv('XDG_CONFIG_HOME') - if (xdgConfigHome) { - def xdgConfigHomeFile = new File(xdgConfigHome) - if (xdgConfigHomeFile.exists()) { - return new File(xdgConfigHomeFile, Constants.CONFIG_FILENAME) - } - } - - def userConfigFile = new File(System.getProperty('user.home'), '.config') - if (userConfigFile.exists()) { - return new File(userConfigFile, Constants.CONFIG_FILENAME) - } - } else if (PlatformService.instance.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 - def roamingConfigHome = System.getenv('APPDATA') - if (roamingConfigHome) { - def roamingConfigHomeFile = new File(roamingConfigHome) - if (roamingConfigHomeFile.exists()) { - return new File(roamingConfigHomeFile, Constants.CONFIG_FILENAME) - } - } - } - - return new File(Constants.CONFIG_FILENAME) - } - - Configuration load() { - // Default values - def screenSize = Toolkit.defaultToolkit.screenSize - - 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 - - def config = new Configuration() - config.mainWindowLocation = new Point(x, y) - config.mainWindowSize = new Dimension(w, h) - config.mainWindowMaximize = false - config.lookAndFeel = 'system' - config.configRecentLoadDirectory = config.configRecentSaveDirectory = new File(System.getProperty('user.dir')) - - try { - // Load values - def configuration = new XmlSlurper().parse(FILE) - if (configuration) { - config.recentFiles = configuration.recentFilePaths?.filePath?.collect({ new File(it.text()) }).grep({ it.exists() }) - - def recentDirectories = configuration.recentDirectories - if (recentDirectories) { - if (recentDirectories.loadPath) { - def loadDirectory = new File(recentDirectories.loadPath.text()) - if (loadDirectory.exists()) - config.configRecentLoadDirectory = loadDirectory - } - if (recentDirectories.savePath) { - def saveDirectory = new File(recentDirectories.savePath.text()) - if (saveDirectory.exists()) - config.configRecentSaveDirectory = saveDirectory - } - } - - config.lookAndFeel = configuration.gui.lookAndFeel ?: System.getProperty('swing.defaultlaf') ?: 'system' - - def mainWindow = configuration.gui.mainWindow - def l = mainWindow.location - def s = mainWindow.size - def m = Boolean.valueOf(mainWindow.maximize.text()) - def lx = l.@x.text() as int, ly = l.@y.text() as int - def sw = s.@w.text() as int, sh = s.@h.text() as int - - if ((lx >= 0) && (ly >= 0) && (lx + sw < screenSize.width) && (ly + sh < screenSize.height)) { - // Update preferences - config.mainWindowLocation = new Point(lx, ly) - config.mainWindowSize = new Dimension(sw, sh) - config.mainWindowMaximize = m - } - - for (def node : configuration.preferences.childNodes()) { - config.preferences.put(node.name(), node.text()) - } - } - } catch (Exception ignore) { - } - - if (! config.preferences.containsKey(ERROR_BACKGROUND_COLOR)) { - config.preferences.put(ERROR_BACKGROUND_COLOR, '0xFF6666') - } - - return config - } - - void save(Configuration configuration) { - Point l = configuration.mainWindowLocation - Dimension s = configuration.mainWindowSize - Writer writer = new StringWriter() - MarkupBuilder xml = new MarkupBuilder(writer) - - xml.configuration() { - gui() { - mainWindow() { - location(x:l.x as int, y:l.y as int) - size(w:s.width as int, h:s.height as int) - maximize configuration.mainWindowMaximize - } - lookAndFeel configuration.lookAndFeel - } - recentFilePaths() { - configuration.recentFiles.each { file -> - filePath file.absolutePath - } - } - recentDirectories() { - loadPath configuration.configRecentLoadDirectory?.absolutePath - savePath configuration.configRecentSaveDirectory?.absolutePath - } - preferences() { - configuration.preferences.each { key, value -> - "$key" "$value" - } - } - } - - FILE.write(writer.toString()) - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/container/ContainerFactoryService.groovy b/app/src/main/groovy/org/jd/gui/service/container/ContainerFactoryService.groovy deleted file mode 100644 index 7327391b..00000000 --- a/app/src/main/groovy/org/jd/gui/service/container/ContainerFactoryService.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class ContainerFactoryService { - protected final Collection providers = ExtensionService.instance.load(ContainerFactory) - - ContainerFactory get(API api, Path rootPath) { - return providers.find { it.accept(api, rootPath) } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/extension/ExtensionService.groovy b/app/src/main/groovy/org/jd/gui/service/extension/ExtensionService.groovy deleted file mode 100644 index 4bb993bd..00000000 --- a/app/src/main/groovy/org/jd/gui/service/extension/ExtensionService.groovy +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic - -@Singleton -@CompileStatic -class ExtensionService { - protected ClassLoader extensionClassLoader = initClassLoader() - - protected ClassLoader initClassLoader() { - def jarUri = ExtensionService.class.protectionDomain.codeSource.location.toURI() - def baseDirectory = new File(jarUri).parent - def extDirectory = new File(baseDirectory, 'ext') - - if (extDirectory.exists() && extDirectory.isDirectory()) { - List urls = [] - - searchJarAndMetaInf(urls, extDirectory) - - if (! urls.isEmpty()) { - URL[] array = urls.sort { u1, u2 -> u1.path.compareTo(u2.path) } - return new URLClassLoader(array, ExtensionService.class.classLoader) - } - } - - return ExtensionService.class.classLoader - } - - protected void searchJarAndMetaInf(List urls, File directory) { - def metaInf = new File(directory, 'META-INF') - - if (metaInf.exists() && metaInf.isDirectory()) { - urls.add(directory.toURI().toURL()) - } else { - directory.eachFile { File child -> - if (child.isDirectory()) { - searchJarAndMetaInf(urls, child) - } else if (child.name.toLowerCase().endsWith('.jar')) { - urls.add(new URL('jar', '', child.toURI().toURL().toString() + '!/')) - } - } - } - } - - public Collection load(Class service) { - return ServiceLoader.load(service, extensionClassLoader).toList() - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/fileloader/FileLoaderService.groovy b/app/src/main/groovy/org/jd/gui/service/fileloader/FileLoaderService.groovy deleted file mode 100644 index dff48317..00000000 --- a/app/src/main/groovy/org/jd/gui/service/fileloader/FileLoaderService.groovy +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class FileLoaderService { - protected final Collection providers = ExtensionService.instance.load(FileLoader) - - protected Map mapProviders = providers.collectEntries { provider -> - provider.extensions.collectEntries { [it, provider] } - } - - FileLoader get(API api, File file) { - String name = file.name - int lastDot = name.lastIndexOf('.') - String extension = name.substring(lastDot+1) - def provider = mapProviders[extension] - - if (provider?.accept(api, file)) { - return provider - } else { - return null - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/indexer/IndexerService.groovy b/app/src/main/groovy/org/jd/gui/service/indexer/IndexerService.groovy deleted file mode 100644 index b16287a0..00000000 --- a/app/src/main/groovy/org/jd/gui/service/indexer/IndexerService.groovy +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.model.Container -import org.jd.gui.service.extension.ExtensionService -import org.jd.gui.spi.Indexer - -@CompileStatic -@Singleton(lazy = true) -class IndexerService { - protected Map mapProviders = populate() - - protected Map populate() { - Collection providers = ExtensionService.instance.load(Indexer) - Map mapProviders = [:] - - def mapProvidersWithDefault = mapProviders.withDefault { new Indexers() } - - for (def provider : providers) { - for (String selector : provider.selectors) { - mapProvidersWithDefault[selector].add(provider) - } - } - - return mapProviders - } - - Indexer get(Container.Entry entry) { - Indexer indexer = get(entry.container.type, entry) - return indexer ?: get('*', entry) - } - - Indexer get(String containerType, Container.Entry entry) { - String path = entry.path - String type = entry.isDirectory() ? 'dir' : 'file' - String prefix = containerType + ':' + type - Indexer indexer = mapProviders.get(prefix + ':' + path)?.match(path) - - if (!indexer) { - int lastSlashIndex = path.lastIndexOf('/') - String name = path.substring(lastSlashIndex+1) - - indexer = mapProviders.get(prefix + ':*/' + name)?.match(path) - - if (!indexer) { - int index = name.lastIndexOf('.') - if (index != -1) { - String extension = name.substring(index + 1) - indexer = mapProviders.get(prefix + ':*.' + extension)?.match(path) - } - if (!indexer) { - indexer = mapProviders.get(prefix + ':*')?.match(path) - } - } - } - - return indexer - } - - static class Indexers { - HashMap indexers = [:] - Indexer defaultIndexer - - void add(Indexer indexer) { - if (indexer.pathPattern) { - indexers.put(indexer.pathPattern.pattern(), indexer) - } else { - defaultIndexer = indexer - } - } - - Indexer match(String path) { - for (def indexer : indexers.values()) { - if (path ==~ indexer.pathPattern) { - return indexer - } - } - return defaultIndexer - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.groovy b/app/src/main/groovy/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.groovy deleted file mode 100644 index 2735fab0..00000000 --- a/app/src/main/groovy/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.groovy +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.SourcesSavable.Controller -import org.jd.gui.api.feature.SourcesSavable.Listener -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.PanelFactory -import org.jd.gui.spi.SourceSaver -import org.jd.gui.spi.TreeNodeFactory -import org.jd.gui.view.component.panel.TreeTabbedPanel - -import javax.swing.JComponent -import javax.swing.tree.DefaultMutableTreeNode -import javax.swing.tree.DefaultTreeModel -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path - -class ContainerPanelFactoryProvider implements PanelFactory { - - @Override String[] getTypes() { ['default'] } - - @Override - public T make(API api, Container container) { - return new ContainerPanel(api, container) - } - - class ContainerPanel extends TreeTabbedPanel implements ContentIndexable, SourcesSavable { - Container.Entry entry - - ContainerPanel(API api, Container container) { - super(api, container.root.uri) - - this.entry = container.root.parent - - def root = new DefaultMutableTreeNode() - - for (def entry : container.root.children) { - TreeNodeFactory factory = api.getTreeNodeFactory(entry) - if (factory) { - root.add(factory.make(api, entry)) - } - } - - tree.model = new DefaultTreeModel(root) - } - - // --- ContentIndexable --- // - @Override - @CompileStatic - Indexes index(API api) { - // Classic map - def map = new HashMap>() - // Map populating value automatically - def mapWithDefault = new HashMap>().withDefault { key -> - def subMap = new HashMap() - - map.put(key, subMap) - - return subMap.withDefault { subKey -> - def array = new ArrayList() - subMap.put(subKey, array) - return array - } - } - // Index populating value automatically - def indexesWithDefault = new Indexes() { - @Override void waitIndexers() {} - @Override Map getIndex(String name) { mapWithDefault.get(name) } - } - - api.getIndexer(entry)?.index(api, entry, indexesWithDefault) - - // To prevent memory leaks, return an index without the 'populate' behaviour - return new Indexes() { - @Override void waitIndexers() {} - @Override Map getIndex(String name) { map.get(name) } - } - } - - // --- SourcesSavable --- // - @Override - String getSourceFileName() { - def path = api.getSourceSaver(entry)?.getSourcePath(entry) - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - @Override int getFileCount() { api.getSourceSaver(entry)?.getFileCount(api, entry) } - - @Override - void save(API api, Controller controller, Listener listener, Path path) { - def parentPath = path.parent - - if (parentPath && !Files.exists(parentPath)) { - Files.createDirectories(parentPath) - } - - def uri = path.toUri() - def archiveUri = new URI('jar:' + uri.scheme, uri.host, uri.path + '!/', null) - def archiveFs = FileSystems.newFileSystem(archiveUri, [create: 'true']) - def archiveRootPath = archiveFs.getPath('/') - - api.getSourceSaver(entry)?.saveContent( - api, - new SourceSaver.Controller() { - boolean isCancelled() { controller.isCancelled() } - }, - new SourceSaver.Listener() { - void pathSaved(Path p) { listener.pathSaved(p) } - }, - archiveRootPath, archiveRootPath, entry - ) - - archiveFs.close() - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/mainpanel/PanelFactoryService.groovy b/app/src/main/groovy/org/jd/gui/service/mainpanel/PanelFactoryService.groovy deleted file mode 100644 index 9b93e8f4..00000000 --- a/app/src/main/groovy/org/jd/gui/service/mainpanel/PanelFactoryService.groovy +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class PanelFactoryService { - protected Map mapProviders = populate() - - protected Map populate() { - Collection providers = ExtensionService.instance.load(PanelFactory) - Map mapProviders = [:] - - for (def provider : providers) { - for (String type : provider.types) { - mapProviders.put(type, provider) - } - } - - return mapProviders - } - - PanelFactory get(Container container) { - return mapProviders.get(container.type) ?: mapProviders.get('default') - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/pastehandler/PasteHandlerService.groovy b/app/src/main/groovy/org/jd/gui/service/pastehandler/PasteHandlerService.groovy deleted file mode 100644 index b21c616e..00000000 --- a/app/src/main/groovy/org/jd/gui/service/pastehandler/PasteHandlerService.groovy +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class PasteHandlerService { - protected final Collection providers = ExtensionService.instance.load(PasteHandler) - - PasteHandler get(Object obj) { - for (def provider : providers) { - if (provider.accept(obj)) { - return provider - } - } - return null - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/platform/PlatformService.groovy b/app/src/main/groovy/org/jd/gui/service/platform/PlatformService.groovy deleted file mode 100644 index 4bcb82b2..00000000 --- a/app/src/main/groovy/org/jd/gui/service/platform/PlatformService.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 - -@Singleton -class PlatformService { - enum OS { Linux, MacOSX, Windows } - - final OS os = initOS() - - final isLinux = (os == OS.Linux) - final isMac = (os == OS.MacOSX) - final isWindows = (os == OS.Windows) - - OS initOS() { - String osName = System.getProperty('os.name').toLowerCase() - - if (osName.contains('windows')) { - return OS.Windows - } else if (osName.contains('mac os')) { - return OS.MacOSX - } else { - return OS.Linux - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/preferencespanel/PreferencesPanelService.groovy b/app/src/main/groovy/org/jd/gui/service/preferencespanel/PreferencesPanelService.groovy deleted file mode 100644 index 257022e5..00000000 --- a/app/src/main/groovy/org/jd/gui/service/preferencespanel/PreferencesPanelService.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class PreferencesPanelService { - final Collection providers = populate() - - protected Collection populate() { - Collection list = ExtensionService.instance.load(PreferencesPanel).grep { it.isActivated() } - Map map = [:] - - for (def provider : list) { - map.put(provider.preferencesGroupTitle + '$' + provider.preferencesPanelTitle, provider) - } - - return map.values() - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.groovy b/app/src/main/groovy/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.groovy deleted file mode 100644 index 2324a74d..00000000 --- a/app/src/main/groovy/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.groovy +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.* -import java.awt.* - -/** - * Single instance is the default mode on Mac OSX, so this panel is not activated. - */ -@CompileStatic -class UISingleInstancePreferencesProvider extends JPanel implements PreferencesPanel { - - static final String SINGLE_INSTANCE = 'UIMainWindowPreferencesProvider.singleInstance' - - JCheckBox singleInstanceTabsCheckBox - - UISingleInstancePreferencesProvider() { - super(new GridLayout(0,1)) - - singleInstanceTabsCheckBox = new JCheckBox('Single instance') - - add(singleInstanceTabsCheckBox) - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'User Interface' } - String getPreferencesPanelTitle() { 'Main window' } - - public void init(Color errorBackgroundColor) {} - - public boolean isActivated() { - !System.getProperty('os.name').toLowerCase().contains('mac os') - } - - void loadPreferences(Map preferences) { - singleInstanceTabsCheckBox.selected = 'true'.equals(preferences.get(SINGLE_INSTANCE)) - } - - void savePreferences(Map preferences) { - preferences.put(SINGLE_INSTANCE, Boolean.toString(singleInstanceTabsCheckBox.selected)) - } - - boolean arePreferencesValid() { true } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} -} diff --git a/app/src/main/groovy/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.groovy b/app/src/main/groovy/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.groovy deleted file mode 100644 index b4e99dea..00000000 --- a/app/src/main/groovy/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.JCheckBox -import javax.swing.JPanel -import java.awt.Color -import java.awt.GridLayout - -/** - * JTabbedPane.WRAP_TAB_LAYOUT is not supported by Aqua L&F. - * This panel is not activated on Mac OSX. - */ -@CompileStatic -class UITabsPreferencesProvider extends JPanel implements PreferencesPanel { - - static final String TAB_LAYOUT = 'UITabsPreferencesProvider.singleLineTabs' - - JCheckBox singleLineTabsCheckBox - - UITabsPreferencesProvider() { - super(new GridLayout(0,1)) - - singleLineTabsCheckBox = new JCheckBox('Tabs on a single line') - - add(singleLineTabsCheckBox) - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'User Interface' } - String getPreferencesPanelTitle() { 'Tabs' } - - public void init(Color errorBackgroundColor) {} - - public boolean isActivated() { - !System.getProperty('os.name').toLowerCase().contains('mac os') - } - - void loadPreferences(Map preferences) { - singleLineTabsCheckBox.selected = 'true'.equals(preferences.get(TAB_LAYOUT)) - } - - void savePreferences(Map preferences) { - preferences.put(TAB_LAYOUT, Boolean.toString(singleLineTabsCheckBox.selected)) - } - - boolean arePreferencesValid() { true } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} -} diff --git a/app/src/main/groovy/org/jd/gui/service/sourcesaver/SourceSaverService.groovy b/app/src/main/groovy/org/jd/gui/service/sourcesaver/SourceSaverService.groovy deleted file mode 100644 index b200d9e4..00000000 --- a/app/src/main/groovy/org/jd/gui/service/sourcesaver/SourceSaverService.groovy +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.model.Container -import org.jd.gui.service.extension.ExtensionService -import org.jd.gui.spi.SourceSaver - -@CompileStatic -@Singleton(lazy = true) -class SourceSaverService { - protected Map mapProviders = populate() - - protected Map populate() { - Collection providers = ExtensionService.instance.load(SourceSaver) - Map mapProviders = [:] - - def mapProvidersWithDefault = mapProviders.withDefault { new SourceSavers() } - - for (def provider : providers) { - for (String selector : provider.selectors) { - mapProvidersWithDefault[selector].add(provider) - } - } - - return mapProviders - } - - SourceSaver get(Container.Entry entry) { - SourceSaver saver = get(entry.container.type, entry) - return saver ?: get('*', entry) - } - - SourceSaver get(String containerType, Container.Entry entry) { - String path = entry.path - String type = entry.isDirectory() ? 'dir' : 'file' - String prefix = containerType + ':' + type - SourceSaver saver = mapProviders.get(prefix + ':' + path)?.match(path) - - if (!saver) { - int lastSlashIndex = path.lastIndexOf('/') - String name = path.substring(lastSlashIndex+1) - - saver = mapProviders.get(prefix + ':*/' + name)?.match(path) - - if (!saver) { - int index = name.lastIndexOf('.') - if (index != -1) { - String extension = name.substring(index + 1) - saver = mapProviders.get(prefix + ':*.' + extension)?.match(path) - } - if (!saver) { - saver = mapProviders.get(prefix + ':*')?.match(path) - } - } - } - - return saver - } - - static class SourceSavers { - HashMap savers = [:] - SourceSaver defaultSaver - - void add(SourceSaver saver) { - if (saver.pathPattern) { - savers.put(saver.pathPattern.pattern(), saver) - } else { - defaultSaver = saver - } - } - - SourceSaver match(String path) { - for (def saver : savers.values()) { - if (path ==~ saver.pathPattern) { - return saver - } - } - return defaultSaver - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/treenode/TreeNodeFactoryService.groovy b/app/src/main/groovy/org/jd/gui/service/treenode/TreeNodeFactoryService.groovy deleted file mode 100644 index 4bbc9910..00000000 --- a/app/src/main/groovy/org/jd/gui/service/treenode/TreeNodeFactoryService.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.model.Container -import org.jd.gui.service.extension.ExtensionService -import org.jd.gui.spi.TreeNodeFactory - -@CompileStatic -@Singleton(lazy = true) -class TreeNodeFactoryService { - protected Map mapProviders = populate() - - protected Map populate() { - Collection providers = ExtensionService.instance.load(TreeNodeFactory) - Map mapProviders = [:] - - def mapProvidersWithDefault = mapProviders.withDefault { new TreeNodeFactories() } - - for (def provider : providers) { - for (String selector : provider.selectors) { - mapProvidersWithDefault[selector].add(provider) - } - } - - return mapProviders - } - - @CompileStatic - TreeNodeFactory get(Container.Entry entry) { - TreeNodeFactory factory = get(entry.container.type, entry) - return factory ?: get('*', entry) - } - - @CompileStatic - TreeNodeFactory get(String containerType, Container.Entry entry) { - String path = entry.path - String type = entry.isDirectory() ? 'dir' : 'file' - String prefix = containerType + ':' + type + ':' - TreeNodeFactory factory = mapProviders.get(prefix + path)?.match(path) - - if (!factory) { - int lastSlashIndex = path.lastIndexOf('/') - String name = path.substring(lastSlashIndex+1) - - factory = mapProviders.get(prefix + '*/' + name)?.match(path) - if (!factory) { - int index = name.lastIndexOf('.') - if (index != -1) { - String extension = name.substring(index + 1) - factory = mapProviders.get(prefix + '*.' + extension)?.match(path) - } - if (!factory) { - factory = mapProviders.get(prefix + '*')?.match(path) - } - } - } - - return factory - } - - static class TreeNodeFactories { - HashMap factories = [:] - TreeNodeFactory defaultFactory - - void add(TreeNodeFactory factory) { - if (factory.pathPattern) { - factories.put(factory.pathPattern.pattern(), factory) - } else { - defaultFactory = factory - } - } - - TreeNodeFactory match(String path) { - for (def factory : factories.values()) { - if (path ==~ factory.pathPattern) { - return factory - } - } - return defaultFactory - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/type/TypeFactoryService.groovy b/app/src/main/groovy/org/jd/gui/service/type/TypeFactoryService.groovy deleted file mode 100644 index 599f5332..00000000 --- a/app/src/main/groovy/org/jd/gui/service/type/TypeFactoryService.groovy +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Type -import org.jd.gui.service.extension.ExtensionService -import org.jd.gui.spi.TypeFactory - -@Singleton(lazy = true) -class TypeFactoryService { - protected Map mapProviders = populate() - - protected Map populate() { - Collection providers = ExtensionService.instance.load(TypeFactory) - Map mapProviders = [:] - - def mapProvidersWithDefault = mapProviders.withDefault { new TypeFactories() } - - for (def provider : providers) { - for (String selector : provider.selectors) { - mapProvidersWithDefault[selector].add(provider) - } - } - - // Early interface loading - Type.class - Type.Field.class - Type.Method.class - - return mapProviders - } - - @CompileStatic - TypeFactory get(Container.Entry entry) { - TypeFactory factory = get(entry.container.type, entry) - return factory ?: get('*', entry) - } - - @CompileStatic - TypeFactory get(String containerType, Container.Entry entry) { - String path = entry.path - String type = entry.isDirectory() ? 'dir' : 'file' - String prefix = containerType + ':' + type + ':' - TypeFactory factory = mapProviders.get(prefix + path)?.match(path) - - if (!factory) { - int lastSlashIndex = path.lastIndexOf('/') - String name = path.substring(lastSlashIndex+1) - - factory = mapProviders.get(prefix + '*/' + name)?.match(path) - - if (!factory) { - int index = name.lastIndexOf('.') - if (index != -1) { - String extension = name.substring(index + 1) - factory = mapProviders.get(prefix + '*.' + extension)?.match(path) - } - if (!factory) { - factory = mapProviders.get(prefix + '*')?.match(path) - } - } - } - - return factory - } - - static class TypeFactories { - HashMap factories = [:] - TypeFactory defaultFactory - - void add(TypeFactory factory) { - if (factory.pathPattern) { - factories.put(factory.pathPattern.pattern(), factory) - } else { - defaultFactory = factory - } - } - - TypeFactory match(String path) { - for (def factory : factories.values()) { - if (path ==~ factory.pathPattern) { - return factory - } - } - return defaultFactory - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/service/uriloader/UriLoaderService.groovy b/app/src/main/groovy/org/jd/gui/service/uriloader/UriLoaderService.groovy deleted file mode 100644 index bcbf0466..00000000 --- a/app/src/main/groovy/org/jd/gui/service/uriloader/UriLoaderService.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 - -@Singleton(lazy = true) -class UriLoaderService { - protected final Collection providers = ExtensionService.instance.load(UriLoader) - - protected Map mapProviders = providers.collectEntries { provider -> - provider.schemes.collectEntries { [it, provider] } - } - - UriLoader get(API api, URI uri) { - def provider = mapProviders[uri.scheme] - - if (provider.accept(api, uri)) { - return provider - } else { - return null - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/util/net/InterProcessCommunications.groovy b/app/src/main/groovy/org/jd/gui/util/net/InterProcessCommunications.groovy deleted file mode 100644 index fc1b0e60..00000000 --- a/app/src/main/groovy/org/jd/gui/util/net/InterProcessCommunications.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 - -class InterProcessCommunications { - - protected static final int PORT = 2015_6 - - void listen(Closure closure) throws Exception { - def listener = new ServerSocket(PORT) - - new Thread().start { - while (true) { - listener.accept().withCloseable { Socket socket -> - closure(new ObjectInputStream(socket.inputStream).readObject()) - } - } - } - } - - void send(Object obj) { - new Socket(InetAddress.localHost, PORT).withCloseable { Socket socket -> - new ObjectOutputStream(socket.outputStream).writeObject(obj) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/util/net/UriUtil.groovy b/app/src/main/groovy/org/jd/gui/util/net/UriUtil.groovy deleted file mode 100644 index 12e533a8..00000000 --- a/app/src/main/groovy/org/jd/gui/util/net/UriUtil.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - -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 - */ - static URI createURI(API api, Collection collectionOfIndexes, Container.Entry entry, String query, String fragment) { - def type = TypeFactoryService.instance.get(entry)?.make(api, entry, fragment) - def uri = entry.uri - def path = type?.outerName ? getOuterPath(collectionOfIndexes, entry, type) : uri.path - return new URI(uri.scheme, uri.host, path, query, fragment) - } - - protected static String getOuterPath(Collection collectionOfIndexes, Container.Entry entry, Type type) { - def outerName = type.outerName - - if (outerName) { - for (def indexes : collectionOfIndexes) { - def outerEntries = indexes.getIndex('typeDeclarations').get(outerName) - if (outerEntries) { - for (def outerEntry : outerEntries) { - if (outerEntry.container == entry.container) { - return outerEntry.uri.path - } - } - } - } - } - - return entry.uri.path - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/GoToDescription.groovy b/app/src/main/groovy/org/jd/gui/view/GoToDescription.groovy deleted file mode 100644 index 980c77b6..00000000 --- a/app/src/main/groovy/org/jd/gui/view/GoToDescription.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 - -dialog( - id:'goToDialog', - owner:mainFrame, - title:'Go to Line', - modal:false, - resizable:false) { - vbox(border:emptyBorder(15)) { - hbox { - label(id: 'goToEnterLineNumberLabel') - hglue() - } - vstrut(10) - textField(id:'goToEnterLineNumberTextField', columns:30) - vstrut(10) - hbox { - label(id:'goToEnterLineNumberErrorLabel', text:' ') - hglue() - } - vstrut(15) - hbox { - hglue() - button(id: 'goToOkButton') { - action(id:'goToOkAction', name:'Ok', enabled:false) - } - hstrut(5) - button { - action(id:'goToCancelAction', name:'Cancel', closure:{ goToDialog.visible = false }) - } - } - vstrut(13) - } -} \ No newline at end of file diff --git a/app/src/main/groovy/org/jd/gui/view/GoToView.groovy b/app/src/main/groovy/org/jd/gui/view/GoToView.groovy deleted file mode 100644 index f1a4ebde..00000000 --- a/app/src/main/groovy/org/jd/gui/view/GoToView.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.feature.LineNumberNavigable -import org.jd.gui.model.configuration.Configuration - -import javax.swing.JComponent -import javax.swing.KeyStroke -import javax.swing.UIManager -import javax.swing.event.DocumentEvent -import javax.swing.event.DocumentListener -import java.awt.Color -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent - -class GoToView { - SwingBuilder swing - Listener listener - - GoToView(SwingBuilder swing, Configuration configuration) { - this.swing = swing - this.listener = new Listener(Color.decode(configuration.preferences.get('JdGuiPreferences.errorBackgroundColor'))) - // Load GUI description - swing.edt { - build(GoToDescription) - goToDialog.with { - rootPane.with { - defaultButton = goToOkButton - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeView.cancel") - actionMap.put("OpenTypeView.cancel", goToCancelAction) - } - pack() - locationRelativeTo = parent - goToEnterLineNumberTextField.addKeyListener(new KeyAdapter() { - void keyTyped(KeyEvent e) { - if (! Character.isDigit(e.keyChar)) { - e.consume() - } - } - }) - } - // Add binding - goToEnterLineNumberTextField.document.addDocumentListener(listener) - } - } - - void show(LineNumberNavigable navigator, Closure okClosure) { - // Init - listener.navigator = navigator - swing.doLater { - goToEnterLineNumberLabel.text = "Enter line number (1..$navigator.maximumLineNumber):" - goToOkAction.closure = { - okClosure(Integer.valueOf(goToEnterLineNumberTextField.text)) - goToDialog.visible = false - } - goToEnterLineNumberTextField.text = '' - goToDialog.visible = true - } - } - - class Listener implements DocumentListener { - LineNumberNavigable navigator - Color backgroundColor - Color errorBackgroundColor - - Listener(Color errorBackgroundColor) { - this.backgroundColor = UIManager.getColor('TextField.background') - this.errorBackgroundColor = errorBackgroundColor - } - - void insertUpdate(DocumentEvent e) { onTextChange() } - void removeUpdate(DocumentEvent e) { onTextChange() } - void changedUpdate(DocumentEvent e) { onTextChange() } - - void onTextChange() { - swing.doLater { - String text = goToEnterLineNumberTextField.text - - if (text.length() == 0) { - goToOkAction.enabled = false - clearErrorMessage() - } else if (text.isInteger()) { - int lineNumber = Integer.valueOf(text) - - if (lineNumber > navigator.maximumLineNumber) { - goToOkAction.enabled = false - showErrorMessage('Line number out of range') - } else if (navigator.checkLineNumber(lineNumber)) { - goToOkAction.enabled = true - clearErrorMessage() - } else { - goToOkAction.enabled = false - showErrorMessage('Line number not found') - } - } else { - goToOkAction.enabled = false - showErrorMessage('Not a number') - } - } - } - - void showErrorMessage(String message) { - swing.with { - goToEnterLineNumberErrorLabel.text = message - goToEnterLineNumberTextField.background = errorBackgroundColor - } - } - - void clearErrorMessage() { - swing.with { - goToEnterLineNumberErrorLabel.text = ' ' - goToEnterLineNumberTextField.background = backgroundColor - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/MainDescription.groovy b/app/src/main/groovy/org/jd/gui/view/MainDescription.groovy deleted file mode 100644 index cab46a51..00000000 --- a/app/src/main/groovy/org/jd/gui/view/MainDescription.groovy +++ /dev/null @@ -1,313 +0,0 @@ -/* - * 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 javax.swing.BorderFactory -import javax.swing.UIManager -import java.awt.BorderLayout -import java.awt.Color -import java.awt.Desktop -import java.awt.Font -import java.awt.GridLayout -import java.awt.Toolkit - -import javax.swing.WindowConstants - -import org.jd.gui.Constants -import org.jd.gui.service.platform.PlatformService - -import java.util.jar.Manifest - -actions { - action( - id:'openAction', - name:'Open File...', - mnemonic:'O', - accelerator:shortcut('O'), - shortDescription:'Open a file', - smallIcon:imageIcon(resource:'/org/jd/gui/images/open.png') - ) - action( - id:'closeAction', - name:'Close', - accelerator:shortcut('W'), - enabled:false - ) - action( - id:'saveAction', - name:'Save', - accelerator:shortcut('S'), - smallIcon:imageIcon(resource:'/org/jd/gui/images/save.png'), - enabled:false - ) - action( - id:'saveAllSourcesAction', - name:'Save All Sources', - accelerator:shortcut('alt S'), - smallIcon:imageIcon(resource:'/org/jd/gui/images/save_all.png'), - enabled:false - ) - action( - id:'exitAction', - name:'Exit', - mnemonic:'x', - accelerator:'alt X', - shortDescription:'Quit this program', - closure:{ System.exit(0) } - ) - action( - id:'copyAction', - name:'Copy', - accelerator:shortcut('C'), - smallIcon:imageIcon(resource:'/org/jd/gui/images/copy.png'), - enabled:false - ) - action( - id:'pasteAction', - name:'Paste Log', - smallIcon:imageIcon(resource:'/org/jd/gui/images/paste.png'), - accelerator:shortcut('V') - ) - action( - id:'selectAllAction', - name:'Select all', - accelerator:shortcut('A'), - enabled:false - ) - action( - id:'findAction', - name:'Find...', - accelerator:shortcut('F'), - enabled:false - ) - action( - id:'openTypeAction', - name:'Open Type...', - accelerator:shortcut('shift T'), - smallIcon:imageIcon(resource:'/org/jd/gui/images/open_type.png'), - enabled:false - ) - action( - id:'openTypeHierarchyAction', - name:'Open Type Hierarchy...', - accelerator:'F4', - enabled:false - ) - action( - id:'goToAction', - name:'Go to Line...', - accelerator:shortcut('L'), - enabled:false - ) - action( - id:'backwardAction', - name:'Back', - accelerator:'alt LEFT', - smallIcon:imageIcon(resource:'/org/jd/gui/images/backward_nav.png'), - enabled:false - ) - action( - id:'forwardAction', - name:'Forward', - accelerator:'alt RIGHT', - smallIcon:imageIcon(resource:'/org/jd/gui/images/forward_nav.png'), - enabled:false - ) - action( - id:'searchAction', - name:'Search...', - accelerator:shortcut('shift S'), - smallIcon:imageIcon(resource:'/org/jd/gui/images/search_src.png'), - enabled:false - ) - action( - id:'wikipediaAction', - name:'Wikipedia', - shortDescription:'Wikipedia', - enabled:Desktop.isDesktopSupported() ? Desktop.desktop.isSupported(Desktop.Action.BROWSE) : false - ) - action( - id:'preferencesAction', - name:'Preferences...', - accelerator:shortcut('shift P'), - shortDescription:'Preferences', - smallIcon:imageIcon(resource:'/org/jd/gui/images/preferences.png') - ) - action( - id:'aboutAction', - name:'About...', - accelerator:'F1', - shortDescription:'About JD-GUI', - closure:{ - aboutDialog.pack() - aboutDialog.setLocationRelativeTo(mainFrame) - aboutDialog.visible = true - } - ) - action( - id:'findNextAction', - name:'Next' - ) - action( - id:'findPreviousAction', - name:'Previous' - ) - action( - id:'findCaseSensitiveAction' - ) - action( - id:'findCloseAction', - closure: { findPanel.visible = false } - ) -} - -frame( - id:'mainFrame', - title:'Java Decompiler', - iconImage:Toolkit.defaultToolkit.getImage(getClass().classLoader.getResource('org/jd/gui/images/jd_icon_128.png')), - minimumSize:[Constants.MINIMAL_WIDTH, Constants.MINIMAL_HEIGHT], - defaultCloseOperation:WindowConstants.EXIT_ON_CLOSE) { - menuBar { - menu('File') { - menuItem(openAction) - separator() - menuItem(closeAction) - separator() - menuItem(saveAction) - menuItem(saveAllSourcesAction) - separator() - menu(id:'recentFiles', text:'Recent Files') - if (!PlatformService.instance.isMac) { - separator() - menuItem(exitAction) - } - } - menu('Edit') { - menuItem(copyAction) - menuItem(pasteAction) - separator() - menuItem(selectAllAction) - separator() - menuItem(findAction) { - } - } - menu('Navigation') { - menuItem(openTypeAction) - menuItem(openTypeHierarchyAction) - separator() - menuItem(goToAction) - separator() - menuItem(backwardAction) - menuItem(forwardAction) - } - menu('Search') { - menuItem(searchAction) - } - menu('Help') { - menuItem(wikipediaAction) - separator() - menuItem(preferencesAction) - if (!PlatformService.instance.isMac) { - separator() - menuItem(aboutAction) - } - } - } - panel { - borderLayout() - toolBar(constraints:PAGE_START, floatable:false, rollover:true) { - iconButton(action:openAction, text:null) - separator() - iconButton(action:openTypeAction, text:null) - iconButton(action:searchAction, text:null) - separator() - iconButton(action:backwardAction, text:null) - iconButton(action:forwardAction, text:null) - } - mainTabbedPanel(id:'mainTabbedPanel', constraints:CENTER, api:api) - hbox(id:'findPanel', constraints:PAGE_END, border:emptyBorder(2), visible:false) { - label(text:'Find: ') - comboBox(id:'findComboBox', editable: true) - hstrut(5) - toolBar(floatable:false, rollover:true) { - iconButton(id:'findNextButton', action:findNextAction, icon:imageIcon(resource: '/org/jd/gui/images/next_nav.png')) - hstrut(5) - iconButton(id:'findPreviousButton', action:findPreviousAction, icon:imageIcon(resource: '/org/jd/gui/images/prev_nav.png')) - } - checkBox(id:'findCaseSensitive', action:findCaseSensitiveAction, text:'Case sensitive') - hglue() - - def closeIcon = imageIcon(resource:'/org/jd/gui/images/close.gif') - def closeActivateIcon = imageIcon(resource:'/org/jd/gui/images/close_active.gif') - - iconButton(id:'findCloseButton', action:findCloseAction, contentAreaFilled:false, icon:closeIcon, rolloverIcon:closeActivateIcon) - } - - if (PlatformService.instance.isMac) { - findPanel.border = BorderFactory.createEmptyBorder(0, 10, 10, 10) - findNextButton.border = findPreviousButton.border = findCloseButton.border = BorderFactory.createEmptyBorder() - } - } -} - -dialog( - id:'aboutDialog', - owner:mainFrame, - title:'About Java Decompiler', - modal:false, - resizable:false) { - panel(border:emptyBorder(15)) { - borderLayout() - vbox(constraints:BorderLayout.NORTH) { - panel( - border:lineBorder(color:Color.BLACK), - background:Color.WHITE) { - borderLayout() - label(icon:imageIcon(resource:'/org/jd/gui/images/jd_icon_64.png'), border:emptyBorder(15), constraints:BorderLayout.WEST) - vbox(border:emptyBorder([15,0,15,15]), constraints:BorderLayout.EAST) { - hbox { - label(text: 'Java Decompiler', font:UIManager.getFont('Label.font').deriveFont(Font.BOLD, 14)) - hglue() - } - hbox { - panel(layout:new GridLayout(2,2), opaque:false, border:emptyBorder([5,10,5,50])) { - def jdGuiVersion = 'SNAPSHOT' - def jdCoreVersion = 'SNAPSHOT' - - getClass().classLoader.getResources('META-INF/MANIFEST.MF').each { uri -> - uri.openStream().withStream { is -> - def attributes = new Manifest(is).mainAttributes - jdGuiVersion = attributes.getValue('JD-GUI-Version') ?: jdGuiVersion - jdCoreVersion = attributes.getValue('JD-Core-Version') ?: jdCoreVersion - } - } - - label(text: 'JD-GUI') - label(text: 'version ' + jdGuiVersion) - label(text: 'JD-Core') - label(text: 'version ' + jdCoreVersion) - } - hglue() - } - hbox { - label(text: 'Copyright © 2008-2019 Emmanuel Dupuy') - hglue() - } - } - } - vstrut(10) - } - hbox(constraints:BorderLayout.SOUTH) { - hglue() - button { - action(id:'aboutOkAction', name:' Ok ', closure:{ aboutDialog.visible = false }) - } - hglue() - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/MainView.groovy b/app/src/main/groovy/org/jd/gui/view/MainView.groovy deleted file mode 100644 index 3f489dd0..00000000 --- a/app/src/main/groovy/org/jd/gui/view/MainView.groovy +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.Constants -import org.jd.gui.api.API -import org.jd.gui.api.feature.ContentSearchable -import org.jd.gui.api.feature.ContentSelectable -import org.jd.gui.api.feature.LineNumberNavigable -import org.jd.gui.api.feature.PageChangeListener -import org.jd.gui.api.feature.PageClosable -import org.jd.gui.api.feature.FocusedTypeGettable -import org.jd.gui.api.feature.ContentSavable -import org.jd.gui.api.feature.ContentCopyable -import org.jd.gui.api.feature.PreferencesChangeListener -import org.jd.gui.api.feature.SourcesSavable -import org.jd.gui.api.feature.UriGettable -import org.jd.gui.api.feature.UriOpenable -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.model.history.History -import org.jd.gui.spi.FileLoader - -import javax.swing.Icon -import javax.swing.JComponent -import javax.swing.JLabel -import javax.swing.event.ChangeEvent -import javax.swing.event.ChangeListener -import java.awt.Color -import java.awt.Dimension -import java.awt.Point -import javax.swing.JFileChooser -import javax.swing.JFrame -import javax.swing.JOptionPane -import javax.swing.filechooser.FileNameExtensionFilter - -import org.jd.gui.view.component.IconButton -import org.jd.gui.view.component.panel.MainTabbedPanel - -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent - -class MainView implements UriOpenable, PreferencesChangeListener { - SwingBuilder swing - History history - Closure openFilesClosure - Color findBackgroundColor - Color findErrorBackgroundColor - - MainView( - SwingBuilder swing, Configuration configuration, API api, History history, - Closure panelClosedClosure, - Closure currentPageChangedClosure, - Closure openFilesClosure, - Closure findCriteriaChangedClosure) { - this.swing = swing - this.history = history - this.openFilesClosure = openFilesClosure - - swing.edt { - // Setup - registerBeanFactory('iconButton', IconButton.class) - registerBeanFactory('mainTabbedPanel', MainTabbedPanel.class) - registerExplicitProperty('api', { api }, {}) // Used to init 'mainTabbedPanel.api' - // Load GUI description - build(MainDescription) - // Add listeners - mainTabbedPanel.pageChangedListeners.add(new PageChangeListener() { - JComponent currentPage = null - - public void pageChanged(T page) { - if (currentPage != page) { - // Update current page - currentPage = page - currentPageChangedClosure(page) - - swing.doLater { - if (page) { - // Update title - def path = page.uri.path - int index = path.lastIndexOf('/') - def name = (index == -1) ? path : path.substring(index + 1) - mainFrame.title = name ? name + ' - Java Decompiler' : 'Java Decompiler' - // Update history - history.add(page.uri) - // Update history actions - updateHistoryActions() - // Update menu - saveAction.enabled = (page instanceof ContentSavable) - copyAction.enabled = (page instanceof ContentCopyable) - selectAllAction.enabled = (page instanceof ContentSelectable) - findAction.enabled = (page instanceof ContentSearchable) - openTypeHierarchyAction.enabled = (page instanceof FocusedTypeGettable) - goToAction.enabled = (page instanceof LineNumberNavigable) - } else { - // Update title - mainFrame.title = 'Java Decompiler' - // Update menu - saveAction.enabled = false - copyAction.enabled = false - selectAllAction.enabled = false - openTypeHierarchyAction.enabled = false - goToAction.enabled = false - } - } - } - } - }) - mainTabbedPanel.tabbedPane.addChangeListener(new ChangeListener() { - int lastTabCount = 0 - - void stateChanged(ChangeEvent e) { - swing.with { - int tabCount = mainTabbedPanel.tabbedPane.tabCount - boolean enabled = (tabCount > 0) - - closeAction.enabled = enabled - openTypeAction.enabled = enabled - searchAction.enabled = enabled - saveAllSourcesAction.enabled = (mainTabbedPanel.tabbedPane.selectedComponent instanceof SourcesSavable) - - if (tabCount < lastTabCount) { - panelClosedClosure() - } - - lastTabCount = tabCount - } - } - }) - mainTabbedPanel.preferencesChanged(configuration.preferences) - findComboBox.editor.editorComponent.addKeyListener(new KeyAdapter() { - String lastStr = '' - - void keyReleased(KeyEvent e) { - def findComboBox = swing.findComboBox - - switch (e.keyCode) { - case KeyEvent.VK_ESCAPE: - swing.findPanel.visible = false - break - case KeyEvent.VK_ENTER: - def str = getFindText() - if (str.length() > 1) { - def index = findComboBox.model.getIndexOf(str) - if(index != -1 ) { - findComboBox.removeItemAt(index) - } - findComboBox.insertItemAt(str, 0) - findComboBox.selectedIndex = 0 - swing.findNextAction.closure() - } - break - default: - def str = getFindText() - if (! lastStr.equals(str)) { - findCriteriaChangedClosure() - lastStr = str - } - } - } - }) - findComboBox.editor.editorComponent.opaque = true - - this.findBackgroundColor = findComboBox.background = findComboBox.editor.editorComponent.background - this.findErrorBackgroundColor = Color.decode(configuration.preferences.get('JdGuiPreferences.errorBackgroundColor')) - } - swing.doLater { - // Lazy initialization - new JLabel('init HTML parser') - } - } - - void show(Point location, Dimension size, boolean maximize) { - swing.edt { - // Set position, resize and show - mainFrame.with { - setLocation(location) - setSize(size) - extendedState = maximize ? JFrame.MAXIMIZED_BOTH : 0 - } - mainFrame.show() - } - } - - void showFindPanel() { - swing.edt { - findPanel.visible = true - findComboBox.requestFocus() - } - } - - void setFindBackgroundColor(boolean wasFound) { - swing.doLater { - findComboBox.editor.editorComponent.background = wasFound ? findBackgroundColor : findErrorBackgroundColor - } - } - - JFileChooser createOpenFileChooser() { - JFileChooser chooser = new JFileChooser() { - void addFileFilters(Map loaders) { - removeChoosableFileFilter(getFileFilter()) - - def extensions = loaders.collect({ key, value -> key }).sort() - def description = extensions.collect({ "*.$it" }).join(', ') - - addChoosableFileFilter(new FileNameExtensionFilter("All files ($description)", extensions as String[])) - - for (def extension : extensions) { - def loader = loaders[extension] - addChoosableFileFilter(new FileNameExtensionFilter(loader.description, loader.extensions)) - } - } - void show(Closure okClosure) { - if (showOpenDialog(swing.mainFrame) == JFileChooser.APPROVE_OPTION) { - okClosure() - } - } - } - return chooser - } - - JFileChooser createSaveFileChooser() { - JFileChooser chooser = new JFileChooser() { - void show(Closure okClosure) { - if (showSaveDialog(swing.mainFrame) == JFileChooser.APPROVE_OPTION) { - if (selectedFile.exists()) { - def title = 'Are you sure?' - def message = "The file '$selectedFile.absolutePath' already isContainsIn.\n Do you want to replace the existing file?" - if (swing.optionPane().showConfirmDialog(swing.mainFrame, message, title, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - okClosure(selectedFile) - } - } else { - okClosure(selectedFile) - } - } - } - } - return chooser - } - - void showErrorDialog(String message) { - swing.optionPane().showMessageDialog(swing.mainFrame, message, 'Error', JOptionPane.ERROR_MESSAGE) - } - - public void addMainPanel(String title, Icon icon, String tip, T component) { - swing.edt { - swing.mainTabbedPanel.addPage(title, icon, tip, component) - } - } - - public List getMainPanels() { - return swing.mainTabbedPanel.getPages() - } - - public T getSelectedMainPanel() { - return swing.mainTabbedPanel.tabbedPane.getSelectedComponent() - } - - void closeCurrentTab() { - swing.doLater { - def component = mainTabbedPanel.tabbedPane.selectedComponent - if (component instanceof PageClosable) { - if (!component.closePage()) { - mainTabbedPanel.removeComponent(component) - } - } else { - mainTabbedPanel.removeComponent(component) - } - } - } - - void updateRecentFilesMenu(List files) { - swing.doLater { - recentFiles.removeAll() - files.each { f -> - recentFiles.add( - menuItem() { - action(name:reduceRecentFilePath(f.absolutePath), closure:{ openFilesClosure(f) }) - } - ) - } - } - } - - String getFindText() { - def doc = swing.findComboBox.editor.editorComponent.document - return doc.getText(0, doc.length) - } - - boolean getFindCaseSensitive() { swing.findCaseSensitive.isSelected() } - - void updateHistoryActions() { - swing.doLater { - backwardAction.enabled = history.canBackward() - forwardAction.enabled = history.canForward() - } - } - - // --- Utils --- // - static String reduceRecentFilePath(String path) { - int lastSeparatorPosition = path.lastIndexOf(File.separatorChar as int) - - 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 --- // - boolean openUri(URI uri) { - boolean success - swing.edt { - success = swing.mainTabbedPanel.openUri(uri) - } - return success - } - - // --- PreferencesChangeListener --- // - void preferencesChanged(Map preferences) { - swing.mainTabbedPanel.preferencesChanged(preferences) - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/OpenTypeDescription.groovy b/app/src/main/groovy/org/jd/gui/view/OpenTypeDescription.groovy deleted file mode 100644 index 78d2be20..00000000 --- a/app/src/main/groovy/org/jd/gui/view/OpenTypeDescription.groovy +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 javax.swing.ScrollPaneConstants -import java.awt.BorderLayout -import java.awt.Dimension - -dialog( - id:'openTypeDialog', - owner:mainFrame, - title:'Open Type', - modal:false) { - panel(border:emptyBorder(15)) { - borderLayout() - vbox(constraints:BorderLayout.NORTH) { - hbox { - label(text:'Select a type to open (* = any string, ? = any character, TZ = TimeZone):') - hglue() - } - vstrut(10) - textField(id:'openTypeEnterTextField', columns:30) - vstrut(10) - hbox { - label(id:'openTypeMatchLabel', text:'Matching types:') - hglue() - } - vstrut(10) - } - scrollPane( - constraints:BorderLayout.CENTER, - horizontalScrollBarPolicy:ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER, - preferredSize:new Dimension(400, 150)) { - list(id:'openTypeList') - } - vbox(constraints:BorderLayout.SOUTH) { - vstrut(25) - hbox { - hglue() - button(id:'openTypeOpenButton') { - action(id:'openTypeOpenAction', name:'Open', enabled:false) - } - hstrut(5) - button { - action(id:'openTypeCancelAction', name:'Cancel', closure:{ openTypeDialog.visible = false }) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyDescription.groovy b/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyDescription.groovy deleted file mode 100644 index 534d66d8..00000000 --- a/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyDescription.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 javax.swing.ScrollPaneConstants -import java.awt.BorderLayout -import java.awt.Dimension - -dialog( - id:'openTypeHierarchyDialog', - owner:mainFrame, - title:'Hierarchy Type', - modal:false) { - panel(border:emptyBorder(15)) { - borderLayout() - scrollPane( - id:'openTypeHierarchyScrollPane', - constraints:BorderLayout.CENTER, - horizontalScrollBarPolicy:ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER, - preferredSize:new Dimension(400, 150)) { - tree(id:'openTypeHierarchyTree') - } - vbox(constraints:BorderLayout.SOUTH) { - vstrut(25) - hbox { - hglue() - button(id:'openTypeHierarchyOpenButton') { - action(id:'openTypeHierarchyOpenAction', name:'Open', enabled:false) - } - hstrut(5) - button { - action(id:'openTypeHierarchyCancelAction', name:'Cancel', closure:{ openTypeHierarchyDialog.visible = false }) - } - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyView.groovy b/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyView.groovy deleted file mode 100644 index 7c60fa22..00000000 --- a/app/src/main/groovy/org/jd/gui/view/OpenTypeHierarchyView.groovy +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.TreeNodeData -import org.jd.gui.api.model.Type -import org.jd.gui.model.configuration.Configuration -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.event.TreeSelectionEvent -import javax.swing.event.TreeSelectionListener -import javax.swing.tree.DefaultMutableTreeNode -import javax.swing.tree.DefaultTreeModel -import javax.swing.tree.TreePath -import java.awt.Point -import java.awt.Rectangle -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent - -class OpenTypeHierarchyView { - static final ImageIcon ROOT_CLASS_ICON = new ImageIcon(OpenTypeHierarchyView.class.classLoader.getResource('org/jd/gui/images/generate_class.png')) - static final ImageIcon ROOT_INTERFACE_ICON = new ImageIcon(OpenTypeHierarchyView.class.classLoader.getResource('org/jd/gui/images/generate_int.png')) - - SwingBuilder swing - API api - Closure onTypeSelectedClosure - Closure getSubTypeNamesClosure - Closure getEntriesClosure - - OpenTypeHierarchyView( - SwingBuilder swing, Configuration configuration, API api, - Closure onTypeSelectedClosure, Closure getSubTypeNamesClosure, Closure getEntriesClosure) { - this.swing = swing - this.api = api - this.onTypeSelectedClosure = onTypeSelectedClosure - this.getSubTypeNamesClosure = getSubTypeNamesClosure - this.getEntriesClosure = getEntriesClosure - // Load GUI description - swing.edt { - // Setup - registerBeanFactory('tree', Tree.class) - // Load GUI description - build(OpenTypeHierarchyDescription) - openTypeHierarchyDialog.with { - rootPane.with { - defaultButton = openTypeHierarchyOpenButton - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeHierarchyView.cancel") - actionMap.put("OpenTypeHierarchyView.cancel", openTypeHierarchyCancelAction) - } - minimumSize = size - openTypeHierarchyTree.model = new DefaultTreeModel(new DefaultMutableTreeNode()) - openTypeHierarchyTree.cellRenderer = new TreeNodeRenderer() - openTypeHierarchyTree.addMouseListener(new MouseAdapter() { - void mouseClicked(MouseEvent e) { - if (e.clickCount == 2) { - onTypeSelected(e.source) - } - } - }) - openTypeHierarchyTree.addTreeExpansionListener(new TreeExpansionListener() { - void treeExpanded(TreeExpansionEvent e) { - def node = e.path.lastPathComponent - def tree = e.source - // Expand node and find the first leaf - while (node.childCount > 0) { - if (node.getChildAt(0).userObject == null) { - // Remove dummy node and create children - populateTreeNode(node, null) - } - if (node.childCount != 1) { - break - } - node = node.getChildAt(0) - } - tree.model.reload(e.path.lastPathComponent) - tree.selectionPath = new TreePath(node.path) - } - void treeCollapsed(TreeExpansionEvent e) {} - }) - openTypeHierarchyTree.addKeyListener(new KeyAdapter() { - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_F4) { - def node = swing.openTypeHierarchyTree.lastSelectedPathComponent - if (node) { - updateTree(node.entry, node.typeName) - } - } - } - }) - openTypeHierarchyTree.addTreeSelectionListener(new TreeSelectionListener() { - void valueChanged(TreeSelectionEvent e) { - swing.openTypeHierarchyOpenAction.enabled = (swing.openTypeHierarchyTree.lastSelectedPathComponent?.entry != null) - } - }) - openTypeHierarchyOpenAction.closure = { onTypeSelected(openTypeHierarchyTree) } - pack() - } - } - } - - void show() { - swing.doLater { - openTypeHierarchyDialog.locationRelativeTo = openTypeHierarchyDialog.parent - openTypeHierarchyDialog.visible = true - openTypeHierarchyTree.requestFocus() - } - } - - boolean isVisible() { swing.openTypeHierarchyDialog.visible } - - void updateTree(Container.Entry entry, String typeName) { - swing.doLater { - // Clear tree - JTree tree = swing.openTypeHierarchyTree - def model = tree.model - def root = model.root - root.removeAllChildren() - - def selectedTreeNode = createTreeNode(entry, typeName) - def parentTreeNode = createParentTreeNode(selectedTreeNode) - - root.add(parentTreeNode) - model.reload() - - if (selectedTreeNode) { - def path = new TreePath(selectedTreeNode.path) - // Expand - tree.expandPath(path) - // Scroll to show tree node - tree.makeVisible(path) - Rectangle bounds = tree.getPathBounds(path) - - if(bounds) { - bounds.x = 0 - - Rectangle lastRowBounds = tree.getRowBounds(tree.getRowCount()-1) - - if (lastRowBounds) { - bounds.y = Math.max(bounds.y-30, 0) - bounds.height = Math.min(bounds.height+bounds.y+60, lastRowBounds.height+lastRowBounds.y) - bounds.y - } - - tree.scrollRectToVisible(bounds) - if (tree.accessibleContext) { - tree.accessibleContext.fireVisibleDataPropertyChange() - } - } - // Select tree node - tree.selectionPath = path - } - } - } - - TreeNode createTreeNode(Container.Entry entry, String typeName) { - def type = api.getTypeFactory(entry).make(api, entry, typeName) - - typeName = type.name - - def entries = getEntriesClosure(typeName) - def treeNode = new TreeNode(entry, typeName, entries, new TreeNodeBean(type)) - def childTypeNames = getSubTypeNamesClosure(typeName) - - if (childTypeNames) { - // Add dummy node - treeNode.add(new DefaultMutableTreeNode()) - } - - return treeNode - } - - /** - * Create parent and sibling tree nodes - */ - TreeNode createParentTreeNode(TreeNode treeNode) { - def type = api.getTypeFactory(treeNode.entry).make(api, treeNode.entry, treeNode.typeName) - def superTypeName = type.superName - - if (superTypeName) { - def superEntries = getEntriesClosure(superTypeName) - - // Search entry in the sane container of 'entry' - def superEntry - - if (superEntries) { - superEntry = superEntries.find { it.container == treeNode.entry.container } - - if (superEntry == null) { - // Not found -> Choose 1st one - superEntry = superEntries.get(0) - } - } else { - superEntry = null - } - - if (superEntry) { - // Create parent tree node - def 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('/') - def package_ = superTypeName.substring(0, lastPackageSeparatorIndex).replace('/', '.') - def name = superTypeName.substring(lastPackageSeparatorIndex + 1).replace('$', '.') - def label = package_ ? name + ' - ' + package_ : name - def icon = ((type.flags & Type.FLAG_INTERFACE) == 0) ? ROOT_CLASS_ICON : ROOT_INTERFACE_ICON - def 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 - */ - 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 - def notNullEntry = superTreeNode.entry ?: activeTreeNode.entry - def preferredContainer = notNullEntry.container - - def activeTypName = activeTreeNode?.typeName - def subTypeNames = getSubTypeNamesClosure(superTreeNode.typeName) - - subTypeNames.collect { tn -> - if (tn.equals(activeTypName)) { - return activeTreeNode - } else { - // Search entry in the sane container of 'superTreeNode.entry' - def entries = getEntriesClosure(tn) - def e = entries.find { it.container == preferredContainer } - if (e == null) { - // Not found -> Choose 1st one - e = entries.get(0) - } - if (e == null) { - return null - } - // Create type - def t = api.getTypeFactory(e).make(api, e, tn) - if (t == null) { - return null - } - // Create tree node - return createTreeNode(e, t.name) - } - }.grep { tn -> - tn != null - }.sort { tn1, tn2 -> - tn1.userObject.label.compareTo(tn2.userObject.label) - }.each { tn -> - superTreeNode.add(tn) - } - } - - void focus() { - swing.doLater { - openTypeHierarchyTree.requestFocus() - } - } - - void onTypeSelected(JTree openTypeHierarchyTree) { - def selectedTreeNode = openTypeHierarchyTree.lastSelectedPathComponent - if (selectedTreeNode) { - def path = new TreePath(selectedTreeNode.path) - Rectangle bounds = openTypeHierarchyTree.getPathBounds(path) - Point listLocation = openTypeHierarchyTree.locationOnScreen - Point leftBottom = new Point(listLocation.x+bounds.x as int, listLocation.y+bounds.y+bounds.height as int) - onTypeSelectedClosure(leftBottom, selectedTreeNode.entries, selectedTreeNode.typeName) - } - } - - 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 - static class TreeNodeBean implements TreeNodeData { - String label - String tip - Icon icon - Icon openIcon - - TreeNodeBean(Type type) { - this.label = type.displayPackageName ? type.displayTypeName + ' - ' + type.displayPackageName : type.displayTypeName - this.icon = type.icon - } - - TreeNodeBean(String label, Icon icon) { - this.label = label - this.icon = icon - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/OpenTypeView.groovy b/app/src/main/groovy/org/jd/gui/view/OpenTypeView.groovy deleted file mode 100644 index 6a733134..00000000 --- a/app/src/main/groovy/org/jd/gui/view/OpenTypeView.groovy +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.model.configuration.Configuration -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.event.ListSelectionEvent -import javax.swing.event.ListSelectionListener -import java.awt.Point -import java.awt.Rectangle -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent - -class OpenTypeView { - static final MAX_LINE_COUNT = 80 - - SwingBuilder swing - API api - - OpenTypeView(SwingBuilder swing, Configuration configuration, API api, Closure onPatternChangedClosure, Closure onTypeSelectedClosure) { - this.swing = swing - this.api = api - // Load GUI description - swing.edt { - // Load GUI description - build(OpenTypeDescription) - openTypeDialog.with { - rootPane.with { - defaultButton = openTypeOpenButton - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "OpenTypeView.cancel") - actionMap.put("OpenTypeView.cancel", openTypeCancelAction) - } - minimumSize = size - openTypeEnterTextField.addKeyListener(new KeyAdapter() { - void keyTyped(KeyEvent e) { - switch (e.keyChar) { - case '=': case '(': case ')': case '{': case '}': case '[': case ']': - e.consume() - break - default: - if (Character.isDigit(e.keyChar) && (swing.openTypeEnterTextField.text.length() == 0)) { - // First character can not be a digit - e.consume() - } - break - } - } - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_DOWN) { - swing.edt { - if (openTypeList.model.size > 0) { - openTypeList.selectedIndex = 0 - openTypeList.requestFocus() - e.consume() - } - } - } - } - }) - openTypeEnterTextField.addFocusListener(new FocusListener() { - void focusGained(FocusEvent e) { - swing.edt { - openTypeList.clearSelection() - } - } - void focusLost(FocusEvent e) {} - }) - openTypeEnterTextField.document.addDocumentListener(new DocumentListener() { - void insertUpdate(DocumentEvent e) { call(e) } - void removeUpdate(DocumentEvent e) { call(e) } - void changedUpdate(DocumentEvent e) { call(e) } - void call(DocumentEvent e) { onPatternChangedClosure(e.document.getText(0, e.document.length)) } - }) - openTypeList.addKeyListener(new KeyAdapter() { - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_UP) { - swing.edt { - if (openTypeList.selectedIndex == 0) { - openTypeEnterTextField.requestFocus() - e.consume() - } - } - } - } - }) - openTypeList.model = new DefaultListModel() - openTypeList.cellRenderer = new OpenTypeListCellRenderer() - openTypeList.addMouseListener(new MouseAdapter() { - void mouseClicked(MouseEvent e) { - if (e.clickCount == 2) { - onTypeSelected(onTypeSelectedClosure) - } - } - }) - openTypeList.addListSelectionListener(new ListSelectionListener() { - void valueChanged(ListSelectionEvent e) { - swing.edt { - openTypeOpenAction.enabled = (openTypeList.selectedValue != null) - } - } - }) - openTypeOpenAction.closure = { onTypeSelected(onTypeSelectedClosure) } - pack() - } - } - } - - void show() { - swing.doLater { - swing.openTypeEnterTextField.selectAll() - // Show - openTypeDialog.locationRelativeTo = openTypeDialog.parent - openTypeDialog.visible = true - openTypeEnterTextField.requestFocus() - } - } - - boolean isVisible() { swing.openTypeDialog.visible } - - String getPattern() { swing.openTypeEnterTextField.text } - - void updateList(Map> map) { - swing.doLater { - def model = openTypeList.model - def typeNames = map.keySet().toList().sort() - - model.removeAllElements() - - typeNames.sort { name1, name2 -> - int lasPackageSeparatorIndex = name1.lastIndexOf('/') - def shortName1 = name1.substring(lasPackageSeparatorIndex+1) - - lasPackageSeparatorIndex = name2.lastIndexOf('/') - def shortName2 = name2.substring(lasPackageSeparatorIndex+1) - - return shortName1.compareTo(shortName2) - }.eachWithIndex { typeName, index -> - if (index < MAX_LINE_COUNT) { - def entries = map.get(typeName) - def firstEntry = entries.iterator().next() - def type = api.getTypeFactory(firstEntry).make(api, firstEntry, typeName) - - if (type) { - model.addElement(new OpenTypeListCellBean(label:type.displayTypeName, packag:type.displayPackageName, icon:type.icon, entries:entries, typeName:typeName)) - } else { - model.addElement(new OpenTypeListCellBean(label:typeName, entries:entries, typeName:typeName)) - } - } else if (index == MAX_LINE_COUNT) { - model.addElement(null) - } - } - - int count = typeNames.size() - switch (count) { - case 0: - openTypeMatchLabel.text = 'Matching types:' - break - case 1: - openTypeMatchLabel.text = '1 matching type:' - break - default: - openTypeMatchLabel.text = count + ' matching types:' - } - } - } - - void focus() { - swing.doLater { - openTypeList.requestFocus() - } - } - - void onTypeSelected(Closure typeSelectedClosure) { - swing.doLater { - def index = openTypeList.selectedIndex - if (index != -1) { - def selectedCellBean = openTypeList.model.getElementAt(index) - Point listLocation = openTypeList.locationOnScreen - Rectangle cellBound = openTypeList.getCellBounds(index, index) - Point leftBottom = new Point(listLocation.x + cellBound.x as int, listLocation.y + cellBound.y + cellBound.height as int) - typeSelectedClosure(leftBottom, selectedCellBean.entries, selectedCellBean.typeName) - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/PreferencesDescription.groovy b/app/src/main/groovy/org/jd/gui/view/PreferencesDescription.groovy deleted file mode 100644 index 4f477fb0..00000000 --- a/app/src/main/groovy/org/jd/gui/view/PreferencesDescription.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 javax.swing.ScrollPaneConstants -import java.awt.BorderLayout - -dialog( - id:'preferencesDialog', - owner:mainFrame, - title:'Preferences', - modal:false) { - panel(border:emptyBorder(15)) { - borderLayout() - scrollPane( - id:'preferencesScrollPane', - constraints:BorderLayout.CENTER, - horizontalScrollBarPolicy:ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER, - border:emptyBorder(0)) { - vbox(id:'preferencesPanels') - } - vbox(constraints:BorderLayout.SOUTH) { - vstrut(15) - hbox { - hglue() - button(id:'preferencesOkButton') { - action(id:'preferencesOkAction', name:' Ok ') - } - hstrut(5) - button { - action(id:'preferencesCancelAction', name:'Cancel', closure:{ preferencesDialog.visible = false }) - } - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/PreferencesView.groovy b/app/src/main/groovy/org/jd/gui/view/PreferencesView.groovy deleted file mode 100644 index 4c21f452..00000000 --- a/app/src/main/groovy/org/jd/gui/view/PreferencesView.groovy +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.BorderFactory -import javax.swing.Box -import javax.swing.JComponent -import javax.swing.JLabel -import javax.swing.KeyStroke -import java.awt.Color -import java.awt.Dimension -import java.awt.Font -import java.awt.Toolkit -import java.awt.event.KeyEvent - -class PreferencesView implements PreferencesPanel.PreferencesPanelChangeListener { - SwingBuilder swing - Map preferences - Collection panels - Map valids - - PreferencesView(SwingBuilder swing, Configuration configuration, Collection panels) { - this.swing = swing - this.preferences = configuration.preferences - this.panels = panels - this.valids = [:] - // Load GUI description - swing.edt { - build(PreferencesDescription) - preferencesDialog.with { - rootPane.with { - defaultButton = preferencesOkButton - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 'PreferencesDescription.cancel') - actionMap.put("PreferencesDescription.cancel", preferencesCancelAction) - } - - preferencesPanels.opaque = true - preferencesPanels.background = preferencesDialog.background - - def groups = [:].withDefault { [] } - def errorBackgroundColor = Color.decode(configuration.preferences.get('JdGuiPreferences.errorBackgroundColor')) - - for (def panel : panels) { - panel.init(errorBackgroundColor) - panel.addPreferencesChangeListener(this) - groups.get(panel.preferencesGroupTitle).add(panel) - } - - for (def groupEntry : groups.entrySet().sort { e1, e2 -> e1.key.compareTo(e2.key) }) { - def vbox = Box.createVerticalBox() - vbox.border = BorderFactory.createTitledBorder(groupEntry.key) - - for (def panel : groupEntry.value.sort { p1, p2 -> p1.preferencesPanelTitle.compareTo(p2.preferencesPanelTitle) }) { - // Add title - def hbox = Box.createHorizontalBox() - def title = new JLabel(panel.preferencesPanelTitle) - title.font = title.font.deriveFont(Font.BOLD) - hbox.add(title) - hbox.add(Box.createHorizontalGlue()) - hbox.border = BorderFactory.createEmptyBorder(0, 0, 5, 0) - vbox.add(hbox) - // Add panel - panel.maximumSize = new Dimension(panel.maximumSize.width as int, panel.preferredSize.height as int) - vbox.add(panel) - } - - preferencesPanels.add(vbox) - } - - preferencesPanels.add(Box.createVerticalGlue()) - - // Size of the screen - def screenSize = Toolkit.defaultToolkit.screenSize - // Height of the task bar - def scnMax = Toolkit.defaultToolkit.getScreenInsets(getGraphicsConfiguration()) - // screen height in pixels without taskbar - int taskBarHeight = scnMax.bottom + scnMax.top - int maxHeight = screenSize.height - taskBarHeight - - int preferredHeight = preferencesPanels.preferredSize.height + 2 - if (preferredHeight > maxHeight) - preferredHeight = maxHeight - preferencesScrollPane.preferredSize = new Dimension(400, preferredHeight) - - minimumSize = new Dimension(300, 200) - - pack() - locationRelativeTo = parent - } - } - } - - void show(Closure okClosure) { - swing.doLater { - preferencesOkAction.closure = { onOk(okClosure) } - for (def panel : panels) { - panel.loadPreferences(preferences) - } - preferencesDialog.visible = true - } - } - - void onOk(Closure okClosure) { - swing.doLater { - for (def panel : panels) { - panel.savePreferences(preferences) - } - preferencesDialog.visible = false - okClosure() - } - } - - // --- PreferencesPanel.PreferencesChangeListener --- // - void preferencesPanelChanged(PreferencesPanel source) { - swing.doLater { - boolean valid = source.arePreferencesValid() - - valids.put(source, Boolean.valueOf(valid)) - - if (valid) { - for (def panel : panels) { - if (valids.get(panel) == Boolean.FALSE) { - preferencesOkAction.enabled = false - return - } - } - preferencesOkAction.enabled = true - } else { - preferencesOkAction.enabled = false - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesDescription.groovy b/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesDescription.groovy deleted file mode 100644 index 4f231de7..00000000 --- a/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesDescription.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 - -dialog( - id:'saveAllSourcesDialog', - owner:mainFrame, - title:'Save All Sources', - modal:false, - resizable:false) { - vbox(border:emptyBorder(15)) { - hbox { - label(id: 'saveAllSourcesLabel', text: ' ') - hglue() - } - vstrut(10) - progressBar(id:'saveAllSourcesProgressBar') - vstrut(15) - hbox { - hglue() - button { - action(id:'saveAllSourcesCancelAction', name:'Cancel') - } - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesView.groovy b/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesView.groovy deleted file mode 100644 index 5b15f87d..00000000 --- a/app/src/main/groovy/org/jd/gui/view/SaveAllSourcesView.groovy +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.model.configuration.Configuration - -import javax.swing.JDialog -import javax.swing.JOptionPane -import java.awt.event.WindowAdapter -import java.awt.event.WindowEvent - -class SaveAllSourcesView { - SwingBuilder swing - API api - - SaveAllSourcesView(SwingBuilder swing, Configuration configuration, API api, Closure cancelClosure) { - this.swing = swing - this.api = api - // Load GUI description - swing.edt { - // Load GUI description - build(SaveAllSourcesDescription) - saveAllSourcesDialog.with { - saveAllSourcesCancelAction.closure = cancelClosure - addWindowListener(new WindowAdapter() { - void windowClosing(WindowEvent e) { - cancelClosure() - } - }) - pack() - } - } - } - - void show(File file) { - swing.doLater { - // Init GUI - saveAllSourcesLabel.text = "Saving '$file.absolutePath'..." - saveAllSourcesProgressBar.value = 0 - saveAllSourcesProgressBar.maximum = 10 - saveAllSourcesProgressBar.indeterminate = true - saveAllSourcesDialog.pack() - // Show - saveAllSourcesDialog.locationRelativeTo = saveAllSourcesDialog.parent - saveAllSourcesDialog.visible = true - } - } - - boolean isVisible() { swing.saveAllSourcesDialog.isVisible() } - - void setMaxValue(int maxValue) { - swing.doLater { - if (maxValue > 0) { - saveAllSourcesProgressBar.maximum = maxValue - saveAllSourcesProgressBar.indeterminate = false - } else { - saveAllSourcesProgressBar.indeterminate = true - } - } - } - - void updateProgressBar(int value) { - swing.doLater { - saveAllSourcesProgressBar.value = value - } - } - - void hide() { - swing.doLater { - saveAllSourcesDialog.visible = false - } - } - - void showActionFailedDialog() { - JOptionPane.showMessageDialog( - swing.saveAllSourcesDialog, '"Save All Sources" action failed.', 'Error', JOptionPane.ERROR_MESSAGE) - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsDescription.groovy b/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsDescription.groovy deleted file mode 100644 index 2b171176..00000000 --- a/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsDescription.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 javax.swing.ScrollPaneConstants -import java.awt.BorderLayout -import java.awt.Dimension - -dialog( - id:'searchInConstantPoolsDialog', - owner:mainFrame, - title:'Search', - modal:false) { - panel(border: emptyBorder(15)) { - borderLayout() - vbox(constraints: BorderLayout.NORTH) { - hbox { - label(text: 'Search string (* = any string, ? = any character):') - hglue() - } - vstrut(10) - hbox { - textField(id: 'searchInConstantPoolsEnterTextField', columns: 30) - } - vstrut(10) - hbox { - panel(border:titledBorder(title:'Search For')) { - borderLayout() - hbox(constraints: BorderLayout.WEST) { - panel { - gridLayout(cols: 1, rows: 2) - checkBox(id: 'searchInConstantPoolsCheckBoxType', text: 'Type', selected:true) - checkBox(id: 'searchInConstantPoolsCheckBoxField', text: 'Field') - } - panel { - gridLayout(cols: 1, rows: 2) - checkBox(id: 'searchInConstantPoolsCheckBoxConstructor', text: 'Constructor') - checkBox(id: 'searchInConstantPoolsCheckBoxMethod', text: 'Method') - } - panel { - gridLayout(cols: 1, rows: 2) - checkBox(id: 'searchInConstantPoolsCheckBoxString', text: 'String Constant') - } - } - } - panel(border:titledBorder(title:'Limit To')) { - borderLayout() - hbox(constraints: BorderLayout.WEST) { - panel { - gridLayout(cols: 1, rows: 2) - checkBox(id: 'searchInConstantPoolsCheckBoxDeclarations', text: 'Declarations', selected:true) - checkBox(id: 'searchInConstantPoolsCheckBoxReferences', text: 'References', selected:true) - } - } - } - } - vstrut(10) - hbox { - label(id:'searchInConstantPoolsLabel', text:'Matching types:') - hglue() - } - vstrut(10) - } - scrollPane( - constraints: BorderLayout.CENTER, - horizontalScrollBarPolicy: ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER, - preferredSize: new Dimension(400, 150)) { - tree(id:'searchInConstantPoolsTree') - } - vbox(constraints: BorderLayout.SOUTH) { - vstrut(25) - hbox { - hglue() - button(id: 'searchInConstantPoolsOkButton') { - action(id: 'searchInConstantPoolsOpenAction', name: 'Open', enabled: false) - } - hstrut(5) - button { - action(id: 'searchInConstantPoolsCancelAction', name: 'Cancel', closure:{ searchInConstantPoolsDialog.visible = false }) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsView.groovy b/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsView.groovy deleted file mode 100644 index c49d98c2..00000000 --- a/app/src/main/groovy/org/jd/gui/view/SearchInConstantPoolsView.groovy +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.api.feature.TreeNodeExpandable -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.model.container.FilteredContainerWrapper -import org.jd.gui.view.component.Tree -import org.jd.gui.view.renderer.TreeNodeRenderer - -import javax.swing.JComponent -import javax.swing.JTree -import javax.swing.KeyStroke -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.TreeModel -import javax.swing.tree.TreePath -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import java.awt.event.ItemEvent -import java.awt.event.ItemListener -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent - -class SearchInConstantPoolsView { - static final int SEARCH_TYPE_TYPE = 1 - static final int SEARCH_TYPE_CONSTRUCTOR = 2 - static final int SEARCH_TYPE_METHOD = 4 - static final int SEARCH_TYPE_FIELD = 8 - static final int SEARCH_TYPE_STRING = 16 - static final int SEARCH_TYPE_DECLARATION = 32 - static final int SEARCH_TYPE_REFERENCE = 64 - - SwingBuilder swing - API api - Closure onTypeSelectedClosure - Set accepted - Set expanded - - SearchInConstantPoolsView( - SwingBuilder swing, Configuration configuration, API api, - Closure onPatternChangedClosure, - Closure onTypeSelectedClosure) { - this.swing = swing - this.api = api - this.onTypeSelectedClosure = onTypeSelectedClosure - // Create listeners - accepted = new HashSet<>() - expanded = new HashSet<>() - // Load GUI description - swing.edt { - // Setup - registerBeanFactory('tree', Tree.class) - // Load GUI description - build(SearchInConstantPoolsDescription) - searchInConstantPoolsDialog.with { - rootPane.with { - defaultButton = searchInConstantPoolsOkButton - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "SearchInConstantPoolsView.cancel") - actionMap.put("SearchInConstantPoolsView.cancel", searchInConstantPoolsCancelAction) - } - minimumSize = size - searchInConstantPoolsEnterTextField.addKeyListener(new KeyAdapter() { - void keyTyped(KeyEvent e) { - switch (e.keyChar) { - case '=': case '(': case ')': case '{': case '}': case '[': case ']': - e.consume() - break - default: - if (Character.isDigit(e.keyChar) && (swing.searchInConstantPoolsEnterTextField.text.length() == 0)) { - // First character can not be a digit - e.consume() - } - break - } - } - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_DOWN) { - swing.edt { - def root = searchInConstantPoolsTree.model.root - if (root.childCount > 0) { - searchInConstantPoolsTree.requestFocus() - if (searchInConstantPoolsTree.selectionCount == 0) { - searchInConstantPoolsTree.selectionPath = new TreePath(root.getChildAt(0).path) - } - e.consume() - } - } - } - } - }) - searchInConstantPoolsEnterTextField.addFocusListener(new FocusListener() { - void focusGained(FocusEvent e) { - swing.edt { - searchInConstantPoolsTree.clearSelection() - searchInConstantPoolsOpenAction.enabled = false - } - } - void focusLost(FocusEvent e) {} - }) - searchInConstantPoolsEnterTextField.document.addDocumentListener(new DocumentListener() { - void insertUpdate(DocumentEvent e) { call() } - void removeUpdate(DocumentEvent e) { call() } - void changedUpdate(DocumentEvent e) { call() } - void call() { onPatternChangedClosure(swing.searchInConstantPoolsEnterTextField.text, flags) } - }) - - def checkBoxListener = new ItemListener() { - void itemStateChanged(ItemEvent e) { - onPatternChangedClosure(swing.searchInConstantPoolsEnterTextField.text, flags) - swing.searchInConstantPoolsEnterTextField.requestFocus() - } - } - - searchInConstantPoolsCheckBoxType.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxField.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxConstructor.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxMethod.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxString.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxDeclarations.addItemListener(checkBoxListener) - searchInConstantPoolsCheckBoxReferences.addItemListener(checkBoxListener) - - searchInConstantPoolsTree.showsRootHandles = true - searchInConstantPoolsTree.addKeyListener(new KeyAdapter() { - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_UP) { - swing.edt { - if (searchInConstantPoolsTree.leadSelectionRow == 0) { - searchInConstantPoolsEnterTextField.requestFocus() - e.consume() - } - } - } - } - }) - searchInConstantPoolsTree.model = new DefaultTreeModel(new DefaultMutableTreeNode()) - searchInConstantPoolsTree.cellRenderer = new TreeNodeRenderer() - searchInConstantPoolsTree.addMouseListener(new MouseAdapter() { - void mouseClicked(MouseEvent e) { - if (e.clickCount == 2) { - def node = e.source.lastSelectedPathComponent - if (node) { - onTypeSelectedClosure(node.uri, swing.searchInConstantPoolsEnterTextField.text, flags) - } - } - } - }) - searchInConstantPoolsTree.addFocusListener(new FocusListener() { - void focusGained(FocusEvent e) { - swing.edt { - searchInConstantPoolsOpenAction.enabled = searchInConstantPoolsTree.selectionCount > 0 - } - } - void focusLost(FocusEvent e) {} - }) - searchInConstantPoolsTree.addTreeExpansionListener(new TreeExpansionListener() { - void treeExpanded(TreeExpansionEvent e) { - def model = e.source.model - def node = e.path.lastPathComponent - // Expand node and find the first leaf - while (true) { - populate(model, node) - if (node.childCount == 0) { - break - } - node = node.getChildAt(0) - } - swing.searchInConstantPoolsTree.selectionPath = new TreePath(node.path) - } - void treeCollapsed(TreeExpansionEvent e) {} - }) - searchInConstantPoolsOpenAction.closure = { onTypeSelected(searchInConstantPoolsTree) } - pack() - } - } - } - - void populate(TreeModel model, DefaultMutableTreeNode node) { - // Populate node - populate(node) - // Populate children - int i = node.childCount - while (i-- > 0) { - def child = node.getChildAt(i) - if ((child instanceof TreeNodeExpandable) && !expanded.contains(child.uri)) { - populate(child) - } - } - // Refresh - model.reload(node) - } - - void populate(DefaultMutableTreeNode node) { - if ((node instanceof TreeNodeExpandable) && !expanded.contains(node.uri)) { - // Populate - node.populateTreeNode(api) - expanded.add(node.uri) - // Filter - int i = node.childCount - while (i-- > 0) { - if (!accepted.contains(node.getChildAt(i).uri)) { - node.remove(i) - } - } - } - } - - void show() { - swing.doLater { - swing.searchInConstantPoolsEnterTextField.selectAll() - // Show - searchInConstantPoolsDialog.locationRelativeTo = searchInConstantPoolsDialog.parent - searchInConstantPoolsDialog.visible = true - searchInConstantPoolsEnterTextField.requestFocus() - } - } - - boolean isVisible() { swing.searchInConstantPoolsDialog.visible } - - String getPattern() { swing.searchInConstantPoolsEnterTextField.text } - - int getFlags() { - int flags = 0 - - if (swing.searchInConstantPoolsCheckBoxType.selected) - flags += SEARCH_TYPE_TYPE - if (swing.searchInConstantPoolsCheckBoxConstructor.selected) - flags += SEARCH_TYPE_CONSTRUCTOR - if (swing.searchInConstantPoolsCheckBoxMethod.selected) - flags += SEARCH_TYPE_METHOD - if (swing.searchInConstantPoolsCheckBoxField.selected) - flags += SEARCH_TYPE_FIELD - if (swing.searchInConstantPoolsCheckBoxString.selected) - flags += SEARCH_TYPE_STRING - if (swing.searchInConstantPoolsCheckBoxDeclarations.selected) - flags += SEARCH_TYPE_DECLARATION - if (swing.searchInConstantPoolsCheckBoxReferences.selected) - flags += SEARCH_TYPE_REFERENCE - - return flags - } - - void updateTree(Collection containers, int matchingTypeCount) { - swing.doLater { - def model = searchInConstantPoolsTree.model - def root = model.root - - // Reset tree nodes - root.removeAllChildren() - - accepted.clear() - expanded.clear() - - if (containers) { - containers.sort { c1, c2 -> - c1.root.uri.compareTo(c2.root.uri) - }.each { container -> - // Init uri set - accepted.addAll(container.uris) - // Populate tree - def parentEntry = container.root.parent - root.add(api.getTreeNodeFactory(parentEntry)?.make(api, parentEntry)) - } - - // Expand node and find the first leaf - def node = root - while (true) { - populate(model, node) - if (node.childCount == 0) { - break - } - node = node.getChildAt(0) - } - searchInConstantPoolsTree.selectionPath = new TreePath(node.path) - } else { - model.reload() - } - - // Update matching item counter - switch (matchingTypeCount) { - case 0: - searchInConstantPoolsLabel.text = 'Matching entries:' - break - case 1: - searchInConstantPoolsLabel.text = '1 matching entry:' - break - default: - searchInConstantPoolsLabel.text = matchingTypeCount + ' matching entries:' - } - } - } - - void onTypeSelected(JTree searchInConstantPoolsTree) { - def selectedTreeNode = searchInConstantPoolsTree.lastSelectedPathComponent - if (selectedTreeNode) { - onTypeSelectedClosure(selectedTreeNode.uri, swing.searchInConstantPoolsEnterTextField.text, flags) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/SelectLocationView.groovy b/app/src/main/groovy/org/jd/gui/view/SelectLocationView.groovy deleted file mode 100644 index b4f2dc09..00000000 --- a/app/src/main/groovy/org/jd/gui/view/SelectLocationView.groovy +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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 groovy.swing.SwingBuilder -import org.jd.gui.api.API -import org.jd.gui.api.feature.TreeNodeExpandable -import org.jd.gui.model.configuration.Configuration -import org.jd.gui.model.container.FilteredContainerWrapper -import org.jd.gui.view.component.Tree -import org.jd.gui.view.renderer.TreeNodeRenderer - -import javax.swing.AbstractAction -import javax.swing.JComponent -import javax.swing.JDialog -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JTree -import javax.swing.KeyStroke -import javax.swing.UIManager -import javax.swing.border.EmptyBorder -import javax.swing.border.LineBorder -import javax.swing.tree.DefaultMutableTreeNode -import javax.swing.tree.DefaultTreeModel -import javax.swing.tree.TreePath -import java.awt.BorderLayout -import java.awt.Color -import java.awt.Dimension -import java.awt.Point -import java.awt.event.ActionEvent -import java.awt.event.FocusAdapter -import java.awt.event.FocusEvent -import java.awt.event.KeyEvent -import java.awt.event.KeyListener -import java.awt.event.MouseEvent -import java.awt.event.MouseListener -import java.awt.event.WindowAdapter -import java.awt.event.WindowEvent - -class SelectLocationView { - SwingBuilder swing - API api - - TreeListener treeListener - DialogClosedListener dialogClosedListener - - JDialog selectLocationDialog - JLabel selectLocationLabel - Tree selectLocationTree - - SelectLocationView(SwingBuilder swing, Configuration configuration, API api) { - this.swing = swing - this.api = api - // Load GUI description - this.treeListener = new TreeListener() - this.dialogClosedListener = new DialogClosedListener() - - Color bg = UIManager.getColor('ToolTip.background') - - selectLocationLabel = new JLabel() - selectLocationLabel.border = new EmptyBorder(5, 5, 0, 5) - - selectLocationTree = new Tree() - selectLocationTree.with { - border = new EmptyBorder(5, 5, 5, 5) - opaque = false - model = new DefaultTreeModel(new DefaultMutableTreeNode()) - cellRenderer = new TreeNodeRenderer() - addKeyListener(treeListener) - addMouseListener(treeListener) - } - - def selectLocationPanel = new JPanel(new BorderLayout()) - selectLocationPanel.with { - border = new LineBorder(bg.darker()) - background = bg - add(selectLocationLabel, BorderLayout.NORTH) - add(selectLocationTree, BorderLayout.CENTER) - } - - selectLocationDialog = new JDialog(swing.mainFrame, '', false) - selectLocationDialog.with { - undecorated = true - add(selectLocationPanel) - rootPane.with { - getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 'SelectLocationView.closeClosure') - actionMap.put('SelectLocationView.closeClosure', new AbstractAction() { - void actionPerformed(ActionEvent e) { selectLocationDialog.visible = false } - }) - } - addWindowListener(dialogClosedListener) - } - - selectLocationTree.addFocusListener(new FocusAdapter() { - void focusLost(FocusEvent e) { selectLocationDialog.visible = false } - }) - } - - void show(Point location, Collection containers, int locationCount, Closure entrySelectedClosure, Closure closeClosure) { - swing.doLater { - treeListener.entrySelectedClosure = entrySelectedClosure - dialogClosedListener.closeClosure = closeClosure - - selectLocationTree.with { - def root = model.root - - // Reset tree nodes - root.removeAllChildren() - - containers.sort { c1, c2 -> - c1.root.uri.compareTo(c2.root.uri) - }.each { container -> - def parentEntry = container.root.parent - def node = api.getTreeNodeFactory(parentEntry)?.make(api, parentEntry) - - if (node) { - root.add(node) - populate(container.uris, node) - } - } - - model.reload() - - // Expand all nodes - for (int row = 0; row < getRowCount(); row++) { - expandRow(row) - } - - // Select first leaf - def node = root - while (true) { - if (node.childCount == 0) { - break - } - node = node.getChildAt(0) - } - selectionPath = new TreePath(node.path) - - // Reset preferred size - preferredSize = null - - // Resize - Dimension ps = preferredSize - if (ps.width < 200) - ps.width = 200 - if (ps.height < 50) - ps.height = 50 - preferredSize = ps - } - - // Update label - selectLocationLabel.text = '' + locationCount + ' locations:' - - selectLocationDialog.with { - pack() - setLocation(location) - visible = true - } - - selectLocationTree.requestFocus() - } - } - - void populate(Set uris, DefaultMutableTreeNode node) { - if (node instanceof TreeNodeExpandable) { - node.populateTreeNode(api) - - int i = node.childCount - - while (i-- > 0) { - def child = node.getChildAt(i) - - if (uris.contains(child.uri)) { - populate(uris, child) - } else { - node.remove(i) - } - } - } - } - - class TreeListener implements KeyListener, MouseListener { - Closure entrySelectedClosure - - void callClosure(JTree tree) { - def node = tree.lastSelectedPathComponent - if (node) { - selectLocationDialog.visible = false - entrySelectedClosure(node.uri) - } - } - - // --- KeyListener --- // - void keyPressed(KeyEvent e) { - if (e.keyCode == KeyEvent.VK_ENTER) { - callClosure(e.source) - } - } - - void keyTyped(KeyEvent e) {} - void keyReleased(KeyEvent e) {} - - // --- MouseListener --- // - void mouseClicked(MouseEvent e) { - if (e.clickCount > 0) { - callClosure(e.source) - } - } - - void mousePressed(MouseEvent e) {} - void mouseReleased(MouseEvent e) {} - void mouseEntered(MouseEvent e) {} - void mouseExited(MouseEvent e) {} - } - - class DialogClosedListener extends WindowAdapter { - Closure closeClosure - - void windowDeactivated(WindowEvent e) { closeClosure() } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/component/List.groovy b/app/src/main/groovy/org/jd/gui/view/component/List.groovy deleted file mode 100644 index b8f3800d..00000000 --- a/app/src/main/groovy/org/jd/gui/view/component/List.groovy +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 sun.swing.DefaultLookup - -import javax.swing.BorderFactory -import javax.swing.JLabel -import javax.swing.JList -import javax.swing.KeyStroke -import javax.swing.ListCellRenderer -import java.awt.Color -import java.awt.Component -import java.awt.Insets -import java.awt.Toolkit -import java.awt.event.KeyEvent - -class List extends JList { - - List() { - super() - - def ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.defaultToolkit.menuShortcutKeyMask) - - inputMap.put(ctrlA, 'none') - inputMap.put(ctrlC, 'none') - inputMap.put(ctrlV, 'none') - setCellRenderer(new Renderer()) - } - - class Renderer implements ListCellRenderer { - Color textSelectionColor - Color backgroundSelectionColor - Color textNonSelectionColor - Color backgroundNonSelectionColor - - JLabel label - - Renderer() { - label = new JLabel() - label.opaque = true - - textSelectionColor = DefaultLookup.getColor(label, ui, "List.dropCellForeground"); - backgroundSelectionColor = DefaultLookup.getColor(label, ui, "List.dropCellBackground"); - textNonSelectionColor = DefaultLookup.getColor(label, ui, "List.foreground"); - backgroundNonSelectionColor = DefaultLookup.getColor(label, ui, "List.background"); - Insets margins = DefaultLookup.getInsets(label, label.ui, "List.contentMargins") - - if (textSelectionColor == null) - textSelectionColor = List.this.getSelectionForeground() - if (backgroundSelectionColor == null) - backgroundSelectionColor = List.this.getSelectionBackground() - - if (margins) { - label.border = BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right) - } else { - label.border = BorderFactory.createEmptyBorder(0, 2, 1, 2) - } - } - - Component getListCellRendererComponent( - JList list, Object value, - int index, boolean selected, boolean hasFocus) { - def data = value.userObject - - if (data instanceof TreeNodeData) { - label.icon = data.icon - - - label.text = data.label - - - } else { - label.icon = null - label.text = '' + data - } - - if (selected) { - label.foreground = textSelectionColor - label.background = backgroundSelectionColor - } else { - label.foreground = textNonSelectionColor - label.background = backgroundNonSelectionColor - } - - return label - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/component/Tree.groovy b/app/src/main/groovy/org/jd/gui/view/component/Tree.groovy deleted file mode 100644 index 15c08ea0..00000000 --- a/app/src/main/groovy/org/jd/gui/view/component/Tree.groovy +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 - -class Tree extends JTree { - Tree() { - def ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.defaultToolkit.menuShortcutKeyMask) - - inputMap.put(ctrlA, 'none') - inputMap.put(ctrlC, 'none') - inputMap.put(ctrlV, 'none') - rootVisible = false - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/component/panel/MainTabbedPanel.groovy b/app/src/main/groovy/org/jd/gui/view/component/panel/MainTabbedPanel.groovy deleted file mode 100644 index c17e6cf6..00000000 --- a/app/src/main/groovy/org/jd/gui/view/component/panel/MainTabbedPanel.groovy +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.feature.PageChangeListener -import org.jd.gui.api.feature.PageChangeable -import org.jd.gui.api.feature.PreferencesChangeListener -import org.jd.gui.api.feature.UriGettable -import org.jd.gui.api.feature.UriOpenable - -import javax.swing.Icon -import javax.swing.JComponent -import javax.swing.event.ChangeEvent -import javax.swing.event.ChangeListener -import java.awt.CardLayout -import java.awt.Color -import java.awt.Font -import javax.swing.BorderFactory -import javax.swing.Box -import javax.swing.BoxLayout -import javax.swing.JLabel -import javax.swing.JPanel - -import org.jd.gui.service.platform.PlatformService - -class MainTabbedPanel extends TabbedPanel implements UriOpenable, PageChangeListener { - List pageChangedListeners = [] - // Flag to prevent the event cascades - boolean pageChangedListenersEnabled = true - - void create() { - Color bg = darker(background) - - if (PlatformService.instance.isWindows) { - background = bg - } - - tabbedPane = createTabPanel() - tabbedPane.addChangeListener(new ChangeListener() { - void stateChanged(ChangeEvent e) { - if (pageChangedListenersEnabled) { - def subPage = tabbedPane.selectedComponent - - if (subPage) { - def page = subPage.getClientProperty('currentPage') - - if (page == null) { - page = tabbedPane.selectedComponent - } - // Fire page changed event - for (def listener : pageChangedListeners) { - listener.pageChanged(page) - } - // Update current sub-page preferences - if (subPage instanceof PreferencesChangeListener) { - subPage.preferencesChanged(preferences) - } - } - } - } - }) - - setLayout(new CardLayout()) - add('panel', createPanel(bg)) - add('tabs', tabbedPane) - - border = BorderFactory.createMatteBorder(1, 0, 0, 0, darker(darker(background))) - } - - JPanel createPanel(Color background) { - JPanel panel = new JPanel() - - String fileManager - - switch (PlatformService.instance.os) { - case PlatformService.OS.Linux: - fileManager = 'your file manager' - break - case PlatformService.OS.MacOSX: - fileManager = 'the Finder' - break - case PlatformService.OS.Windows: - fileManager = 'Explorer' - break - } - - panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)) - panel.background = background - - Color fontColor = panel.background.darker() - - panel.add(Box.createHorizontalGlue()) - - JPanel box = new JPanel() - box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)) - box.background = panel.background - box.add(Box.createVerticalGlue()) - - JLabel title = new JLabel(text:'No files are open', foreground:fontColor) - title.font = title.font.deriveFont(Font.BOLD, title.font.size+8) - - box.add(title) - box.add(new JLabel()) - box.add(new JLabel(text:'Open a file with menu "File > Open File..."', foreground:fontColor)) - box.add(new JLabel(text:'Open recent files with menu "File > Recent Files"', foreground:fontColor)) - box.add(new JLabel(text:'Drag and drop files from ' + fileManager, foreground:fontColor)) - box.add(Box.createVerticalGlue()) - - panel.add(box) - panel.add(Box.createHorizontalGlue()) - - return panel - } - - public void addPage(String title, Icon icon, String tip, T page) { - super.addPage(title, icon, tip, page) - if (page instanceof PageChangeable) { - page.addPageChangeListener(this) - } - } - - public List getPages() { - int i = tabbedPane.getTabCount() - def pages = new ArrayList(i) - while (i-- > 0) { - pages.add(tabbedPane.getComponentAt(i)) - } - return pages - } - - // --- URIOpener --- // - boolean openUri(URI uri) { - try { - // Disable page changed event - pageChangedListenersEnabled = false - // Search & display main tab - def page = showPage(uri) - if (page) { - if (!uri.equals(page.uri) && (page instanceof UriOpenable)) { - // Enable page changed event - pageChangedListenersEnabled = true - // Search & display sub tab - return page.openUri(uri) - } - return true - } else { - return false - } - } finally { - // Enable page changed event - pageChangedListenersEnabled = true - } - } - - // --- PageChangedListener --- // - public void pageChanged(T page) { - // Store active page for current sub tabbed pane - tabbedPane.selectedComponent?.putClientProperty('currentPage', page) - // Forward event - for (def listener : pageChangedListeners) { - listener.pageChanged(page) - } - } - - // --- PreferencesChangeListener --- // - void preferencesChanged(Map preferences) { - super.preferencesChanged(preferences) - - // Update current sub-page preferences - def subPage = tabbedPane.selectedComponent - if (subPage instanceof PreferencesChangeListener) { - subPage.preferencesChanged(preferences) - } - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/component/panel/TabbedPanel.groovy b/app/src/main/groovy/org/jd/gui/view/component/panel/TabbedPanel.groovy deleted file mode 100644 index 4d87fc1d..00000000 --- a/app/src/main/groovy/org/jd/gui/view/component/panel/TabbedPanel.groovy +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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.PreferencesChangeListener -import org.jd.gui.api.feature.UriGettable -import org.jd.gui.service.platform.PlatformService - -import javax.swing.AbstractAction -import javax.swing.JComponent -import javax.swing.JMenu -import javax.swing.JMenuItem -import javax.swing.JPopupMenu -import javax.swing.ToolTipManager -import javax.swing.event.ChangeEvent -import java.awt.BorderLayout -import java.awt.CardLayout -import java.awt.Color -import java.awt.Component -import java.awt.Font -import java.awt.event.ActionEvent -import java.awt.event.ActionListener - -import javax.swing.BorderFactory -import javax.swing.Icon -import javax.swing.ImageIcon -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JTabbedPane -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent -import java.awt.event.MouseListener - -class TabbedPanel extends JPanel implements PreferencesChangeListener { - static final ImageIcon CLOSE_ICON = new ImageIcon(TabbedPanel.class.classLoader.getResource('org/jd/gui/images/close.gif')) - static final ImageIcon CLOSE_ACTIVE_ICON = new ImageIcon(TabbedPanel.class.classLoader.getResource('org/jd/gui/images/close_active.gif')) - - static final String TAB_LAYOUT = 'UITabsPreferencesProvider.singleLineTabs' - - API api - JTabbedPane tabbedPane - Map preferences - - TabbedPanel() { - create() - } - - void create() { - setLayout(new CardLayout()) - add('panel', createPanel(background)) - add('tabs', tabbedPane = createTabPanel()) - } - - JPanel createPanel(Color background) { - return new JPanel() - } - - JTabbedPane createTabPanel() { - JTabbedPane tabPanel = new JTabbedPane() { - String getToolTipText(MouseEvent e) { - int index = indexAtLocation(e.x, e.y) - if (index != -1) { - return getTabComponentAt(index).toolTipText - } - return super.getToolTipText(e) - } - } - ToolTipManager.sharedInstance().registerComponent(tabPanel) - tabPanel.addMouseListener(new MouseAdapter() { - void mousePressed(MouseEvent e) { showPopupTabMenu(e) } - void mouseReleased(MouseEvent e) { showPopupTabMenu(e) } - void showPopupTabMenu(MouseEvent e) { - if (e.isPopupTrigger()) { - int index = tabPanel.indexAtLocation(e.x, e.y) - if (index != -1) { - new PopupTabMenu(tabPanel.getComponentAt(index)).show(e.component, e.x, e.y) - } - } - } - }) - return tabPanel - } - - static Color darker(Color c) { - return new Color( - Math.max((int)(c.red *0.85), 0), - Math.max((int)(c.green*0.85), 0), - Math.max((int)(c.blue *0.85), 0), - c.alpha) - } - - public void addPage(String title, Icon icon, String tip, T page) { - // Update preferences - if (page instanceof PreferencesChangeListener) { - page.preferencesChanged(preferences) - } - // Add a new tab - JLabel tabCloseButton = new JLabel(CLOSE_ICON) - tabCloseButton.toolTipText = 'Close this panel' - tabCloseButton.addMouseListener(new MouseListener() { - void mousePressed(MouseEvent e) {} - void mouseReleased(MouseEvent e) {} - void mouseEntered(MouseEvent e) { e.source.icon = TabbedPanel.@CLOSE_ACTIVE_ICON } - void mouseExited(MouseEvent e) { e.source.icon = TabbedPanel.@CLOSE_ICON } - void mouseClicked(MouseEvent e) { removeComponent(page) } - }) - - JPanel tab = new JPanel(new BorderLayout()) - tab.border = BorderFactory.createEmptyBorder(2, 0, 3, 0) - tab.opaque = false - tab.toolTipText = 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) - - getLayout().show(this, 'tabs') - } - - void setSelectedIndex(int index) { - if (index != -1) { - if (tabbedPane.tabLayoutPolicy == JTabbedPane.SCROLL_TAB_LAYOUT) { - // Ensure that the new page is visible (bug with SCROLL_TAB_LAYOUT) - def event = new ChangeEvent(tabbedPane) - for (def listener : tabbedPane.getChangeListeners()) { - if (listener.class.package.name.startsWith('javax.')) - listener.stateChanged(event) - } - } - - tabbedPane.selectedIndex = index - } - } - - Object showPage(URI uri) { - def u1 = uri.toString() - int i = tabbedPane.getTabCount() - - while (i-- > 0) { - def page = tabbedPane.getComponentAt(i) - def u2 = page.uri.toString() - if (u1.startsWith(u2)) { - tabbedPane.setSelectedIndex(i) - return page - } - } - - return null - } - - class PopupTabMenu extends JPopupMenu { - PopupTabMenu(Component component) { - // Add default popup menu entries - def menuItem = new JMenuItem('Close', null) - menuItem.addActionListener(new ActionListener() { - void actionPerformed(ActionEvent e) { removeComponent(component) } - }) - add(menuItem) - - menuItem = new JMenuItem('Close Others', null) - menuItem.addActionListener(new ActionListener() { - void actionPerformed(ActionEvent e) { removeOtherComponents(component) } - }) - add(menuItem) - - menuItem = new JMenuItem('Close All', null) - menuItem.addActionListener(new ActionListener() { - void actionPerformed(ActionEvent e) { removeAllComponents() } - }) - add(menuItem) - - // Add 'Select Tab' popup menu entry - if ((tabbedPane.tabCount > 1) && (PlatformService.instance.isMac || 'true'.equals(preferences.get(TAB_LAYOUT)))) { - addSeparator() - def menu = new JMenu('Select Tab') - int count = tabbedPane.tabCount - - for (int i=0; i 0) { - def c = tabbedPane.getComponentAt(i) - if (c != component) { - tabbedPane.remove(i) - } - } - if (tabbedPane.tabCount == 0) { - getLayout().show(this, 'panel') - } - } - - void removeAllComponents() { - tabbedPane.removeAll() - if (tabbedPane.tabCount == 0) { - getLayout().show(this, 'panel') - } - } - - // --- PreferencesChangeListener --- // - void preferencesChanged(Map preferences) { - // Store preferences - this.preferences = preferences - // Update layout - if ('true'.equals(preferences.get(TAB_LAYOUT))) { - tabbedPane.tabLayoutPolicy = JTabbedPane.SCROLL_TAB_LAYOUT - } else { - tabbedPane.tabLayoutPolicy = JTabbedPane.WRAP_TAB_LAYOUT - } - setSelectedIndex(tabbedPane.selectedIndex) - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/component/panel/TreeTabbedPanel.groovy b/app/src/main/groovy/org/jd/gui/view/component/panel/TreeTabbedPanel.groovy deleted file mode 100644 index e718acf3..00000000 --- a/app/src/main/groovy/org/jd/gui/view/component/panel/TreeTabbedPanel.groovy +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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.PageChangeListener -import org.jd.gui.api.feature.PageChangeable -import org.jd.gui.api.feature.PageClosable -import org.jd.gui.api.feature.PreferencesChangeListener -import org.jd.gui.api.feature.TreeNodeExpandable -import org.jd.gui.api.feature.UriGettable -import org.jd.gui.api.model.TreeNodeData -import org.jd.gui.api.feature.PageCreator -import org.jd.gui.api.feature.UriOpenable -import org.jd.gui.view.component.Tree -import org.jd.gui.view.renderer.TreeNodeRenderer - -import javax.swing.* -import javax.swing.event.ChangeEvent -import javax.swing.event.ChangeListener -import javax.swing.event.TreeExpansionEvent -import javax.swing.event.TreeExpansionListener -import javax.swing.event.TreeSelectionEvent -import javax.swing.event.TreeSelectionListener -import javax.swing.tree.DefaultMutableTreeNode -import java.awt.* -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent -import java.util.List - -class TreeTabbedPanel extends JPanel implements UriGettable, UriOpenable, PageChangeable, PageClosable, PreferencesChangeListener { - API api - URI uri - Tree tree - TabbedPanel tabbedPanel - List pageChangedListeners = [] - // Flags to prevent the event cascades - boolean updateTreeMenuEnabled = true - boolean openUriEnabled = true - boolean treeNodeChangedEnabled = true - - TreeTabbedPanel(API api, URI uri) { - this.api = api - this.uri = uri - - tree = new Tree() - tree.showsRootHandles = true - tree.setMinimumSize([150, 10] as Dimension) - tree.expandsSelectedPaths = true - tree.cellRenderer = new TreeNodeRenderer() { - 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(new TreeSelectionListener() { - void valueChanged(TreeSelectionEvent e) { treeNodeChanged(tree.lastSelectedPathComponent) } - }) - tree.addTreeExpansionListener(new TreeExpansionListener() { - void treeExpanded(TreeExpansionEvent e) { - def node = e.path.lastPathComponent - if (node instanceof TreeNodeExpandable) { - def oldList = node.children().toList() - node.populateTreeNode(api); - def newList = node.children().toList() - if (! oldList.equals(newList)) { - tree.model.reload(node) - } - } - } - void treeCollapsed(TreeExpansionEvent e) {} - }) - tree.addMouseListener(new MouseAdapter() { - void mouseClicked(MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { - def path = tree.getPathForLocation(e.x, e.y) - - if (path) { - tree.selectionPath = path - - def node = path.lastPathComponent - def actions = api.getContextualActions(node.entry, node.uri.fragment) - - if (actions) { - def popup = new JPopupMenu() - for (def action : actions) { - if (action) { - popup.add(action) - } else { - popup.addSeparator() - } - } - popup.show(e.component, e.x, e.y) - } - } - } - } - }) - - tabbedPanel = new TabbedPanel() - tabbedPanel.api = api - tabbedPanel.setMinimumSize([150, 10] as Dimension) - tabbedPanel.tabbedPane.addChangeListener(new ChangeListener() { - void stateChanged(ChangeEvent e) { pageChanged() } - }) - - layout = new BorderLayout() - - JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(tree), tabbedPanel) - splitter.resizeWeight = 0.2 - - add(splitter, BorderLayout.CENTER) - } - - protected void treeNodeChanged(T node) { - if (treeNodeChangedEnabled && node) { - try { - // Disable tabbedPane.changeListener - updateTreeMenuEnabled = false - - // Search base tree node - def uri = node.uri - - if ((uri.fragment == null) && (uri.query == null)) { - showPage(uri, uri, node) - } else { - def baseUri = new URI(uri.scheme, uri.host, uri.path, null) - def baseNode = node - - while (!baseNode?.uri.equals(baseUri)) { - baseNode = baseNode.parent - } - if (baseNode?.uri.equals(baseUri)) { - showPage(uri, baseUri, baseNode) - } - } - } finally { - // Enable tabbedPane.changeListener - updateTreeMenuEnabled = true - } - } - } - - protected boolean showPage(URI uri, URI baseUri, DefaultMutableTreeNode baseNode) { - def page = tabbedPanel.showPage(baseUri) - - if ((page == null) && (baseNode instanceof PageCreator)) { - page = baseNode.createPage(api) - page.putClientProperty('node', baseNode) - page.putClientProperty('preferences-stamp', Integer.valueOf(api.preferences.hashCode())) - page.putClientProperty('collectionOfIndexes-stamp', Integer.valueOf(api.collectionOfIndexes.hashCode())) - - def path = baseUri.path - def label = path.substring(path.lastIndexOf('/')+1) - def data = baseNode.userObject - - if (data instanceof TreeNodeData) { - tabbedPanel.addPage(label, data.icon, data.tip, page) - } else { - tabbedPanel.addPage(label, null, null, page) - } - } - - if (openUriEnabled && page instanceof UriOpenable) { - page.openUri(uri) - } - - return (page != null) - } - - void pageChanged() { - try { - // Disable highlight - openUriEnabled = false - - def page = tabbedPanel.tabbedPane.selectedComponent - - if (updateTreeMenuEnabled) { - // Synchronize tree - if (page) { - def node = page.getClientProperty('node') - // Select tree node - tree.selectionPath = node.path - tree.scrollPathToVisible(tree.selectionPath) - } else { - tree.clearSelection() - } - } - // Fire page changed event - for (def listener : pageChangedListeners) { - listener.pageChanged(page) - } - } finally { - // Enable highlight - openUriEnabled = true - } - } - - // --- URIGetter --- // - URI getUri() { uri } - - // --- URIOpener --- // - boolean openUri(URI uri) { - def baseUri = new URI(uri.scheme, uri.host, uri.path, null) - def baseNode = searchTreeNode(baseUri, tree.model.root) - - if (baseNode && showPage(uri, baseUri, baseNode)) { - def node = searchTreeNode(uri, baseNode) - if (node) { - try { - // Disable tree node changed listener - treeNodeChangedEnabled = false - // Select tree node - tree.selectionPath = node.path - tree.scrollPathToVisible(tree.selectionPath) - } finally { - // Enable tree node changed listener - treeNodeChangedEnabled = true - } - } - return true - } else { - return false - } - } - - protected DefaultMutableTreeNode searchTreeNode(URI uri, DefaultMutableTreeNode node) { - if (node instanceof TreeNodeExpandable) { - node.populateTreeNode(api) - } - - def u = uri.toString() - def child = node.children().find { - def childU = it.uri.toString() - - if (u.length() > childU.length()) { - if (u.startsWith(childU)) { - char c = u.charAt(childU.length()) - return (c == '/') || (c == '!') - } else { - return false - } - } else { - return u.equals(childU) - } - } - - if (child) { - if (u.equals(child.uri.toString())) { - return child - } else { - // Parent tree node found -> Recursive call - return searchTreeNode(uri, child) - } - } else { - // Not found - return null - } - } - - // --- PageChanger --- // - void addPageChangeListener(PageChangeListener listener) { - pageChangedListeners.add(listener) - } - - // --- PageCloser --- // - boolean closePage() { - def component = tabbedPanel.tabbedPane.selectedComponent - if (component) { - tabbedPanel.removeComponent(component) - return true - } else { - return false - } - } - - // --- PreferencesChangeListener --- // - void preferencesChanged(Map preferences) { - tabbedPanel.preferencesChanged(preferences) - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/renderer/OpenTypeListCellRenderer.groovy b/app/src/main/groovy/org/jd/gui/view/renderer/OpenTypeListCellRenderer.groovy deleted file mode 100644 index cc5d9163..00000000 --- a/app/src/main/groovy/org/jd/gui/view/renderer/OpenTypeListCellRenderer.groovy +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.JLabel -import javax.swing.JList -import javax.swing.JPanel -import javax.swing.ListCellRenderer -import javax.swing.UIManager -import java.awt.BorderLayout -import java.awt.Color -import java.awt.Component - -class OpenTypeListCellRenderer implements ListCellRenderer { - Color textSelectionColor - Color textNonSelectionColor - Color infoSelectionColor - Color infoNonSelectionColor - Color backgroundSelectionColor - Color backgroundNonSelectionColor - - JPanel panel - JLabel label, info - - StringBuffer sb = new StringBuffer() - - 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.red + c.green + c.blue > (3*127)) { - return new Color( - (int)((c.red-127) *0.7 + 127), - (int)((c.green-127)*0.7 + 127), - (int)((c.blue-127) *0.7 + 127), - c.alpha) - } else { - return new Color( - (int)(127 - (127-c.red) *0.7), - (int)(127 - (127-c.green)*0.7), - (int)(127 - (127-c.blue) *0.7), - c.alpha) - } - } - - Component getListCellRendererComponent( - JList list, OpenTypeListCellBean value, - int index, boolean selected, boolean hasFocus) { - - if (value) { - // Display first level item - label.text = value.label - label.icon = value.icon - - sb.setLength(0) - if (value.packag) { - sb.append(' - ').append(value.packag) - } - info.text = sb.toString() - - if (selected) { - label.foreground = textSelectionColor - info.foreground = infoSelectionColor - panel.background = backgroundSelectionColor - } else { - label.foreground = textNonSelectionColor - info.foreground = infoNonSelectionColor - panel.background = backgroundNonSelectionColor - } - } else { - label.text = ' ...' - label.icon = null - info.text = '' - label.foreground = textNonSelectionColor - panel.background = backgroundNonSelectionColor - } - - return panel - } -} diff --git a/app/src/main/groovy/org/jd/gui/view/renderer/TreeNodeRenderer.groovy b/app/src/main/groovy/org/jd/gui/view/renderer/TreeNodeRenderer.groovy deleted file mode 100644 index e28e6145..00000000 --- a/app/src/main/groovy/org/jd/gui/view/renderer/TreeNodeRenderer.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.BorderFactory -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JTree -import javax.swing.UIManager -import javax.swing.tree.TreeCellRenderer -import java.awt.BorderLayout -import java.awt.Color -import java.awt.Component -import java.awt.Insets - -class TreeNodeRenderer implements TreeCellRenderer { - Color textSelectionColor - Color backgroundSelectionColor - Color textNonSelectionColor - Color backgroundNonSelectionColor - Color textDisabledColor - Color backgroundDisabledColor - - JPanel panel - JLabel icon, label - - TreeNodeRenderer() { - panel = new JPanel(new BorderLayout()) - panel.add(icon = new JLabel(), BorderLayout.WEST) - panel.add(label = new JLabel(), BorderLayout.CENTER) - panel.opaque = 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.foreground = textNonSelectionColor - icon.opaque = label.opaque = false - icon.border = BorderFactory.createEmptyBorder(0, 0, 0, 2) - - if (margins) { - label.border = BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right) - } else { - label.border = BorderFactory.createEmptyBorder(0, 4, 0, 4) - } - } - - Component getTreeCellRendererComponent( - JTree tree, Object value, - boolean selected, boolean expanded, - boolean leaf, int row, boolean hasFocus) { - def data = value.userObject - - if (data instanceof TreeNodeData) { - icon.icon = expanded ? (data.openIcon ?: data.icon) : data.icon - label.text = data.label - } else { - icon.icon = null - label.text = '' + data - } - - if (selected) { - if (hasFocus) { - label.foreground = textSelectionColor - label.background = backgroundSelectionColor - } else { - label.foreground = textDisabledColor - label.background = backgroundDisabledColor - } - label.opaque = true - } else { - label.foreground = textNonSelectionColor - label.opaque = false - } - - return panel - } -} diff --git a/app/src/main/java/org/jd/gui/App.java b/app/src/main/java/org/jd/gui/App.java new file mode 100644 index 00000000..fb61686d --- /dev/null +++ b/app/src/main/java/org/jd/gui/App.java @@ -0,0 +1,89 @@ +/* + * 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(); + controller.openFiles(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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/Constants.java b/app/src/main/java/org/jd/gui/Constants.java new file mode 100644 index 00000000..75d5cdd1 --- /dev/null +++ b/app/src/main/java/org/jd/gui/Constants.java @@ -0,0 +1,23 @@ +/* + * 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; +} diff --git a/app/src/main/java/org/jd/gui/OsxApp.java b/app/src/main/java/org/jd/gui/OsxApp.java new file mode 100644 index 00000000..6b677c10 --- /dev/null +++ b/app/src/main/java/org/jd/gui/OsxApp.java @@ -0,0 +1,27 @@ +/* + * 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.*; + +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)); + } +} diff --git a/app/src/main/java/org/jd/gui/controller/AboutController.java b/app/src/main/java/org/jd/gui/controller/AboutController.java new file mode 100644 index 00000000..64952e86 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/AboutController.java @@ -0,0 +1,26 @@ +/* + * 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(); + } +} diff --git a/app/src/main/java/org/jd/gui/controller/GoToController.java b/app/src/main/java/org/jd/gui/controller/GoToController.java new file mode 100644 index 00000000..04c92b2e --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/GoToController.java @@ -0,0 +1,29 @@ +/* + * 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); + } +} diff --git a/app/src/main/java/org/jd/gui/controller/MainController.java b/app/src/main/java/org/jd/gui/controller/MainController.java new file mode 100644 index 00000000..7a9de5f5 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/MainController.java @@ -0,0 +1,653 @@ +/* + * 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.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.*; +import java.net.URI; +import java.nio.file.Path; +import java.util.*; +import java.util.List; +import java.util.concurrent.*; + +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 History history = new History(); + protected JComponent currentPage = null; + protected ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + protected ArrayList containerChangeListeners = new ArrayList<>(); + + long debug; + + @SuppressWarnings("unchecked") + public MainController(Configuration configuration) { + debug = System.currentTimeMillis(); + 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 -> onWikipedia(), + e -> onPreferences(), + e -> onAbout(), + () -> panelClosed(), + page -> onCurrentPageChanged((JComponent)page), + file -> openFile((File)file)); + }); + } + + // --- Show GUI --- // + @SuppressWarnings("unchecked") + public void show() { + SwingUtil.invokeLater(() -> { + // Show main frame + mainView.show( + configuration.getMainWindowLocation(), + configuration.getMainWindowSize(), + configuration.isMainWindowMaximize()); + }); + + // Background initializations + executor.schedule(() -> { + // Background service initialization + ContextualActionsFactoryService.getInstance(); + ContainerFactoryService.getInstance(); + FileLoaderService.getInstance(); + IndexerService.getInstance(); + PasteHandlerService.getInstance(); + PreferencesPanelService.getInstance(); + TreeNodeFactoryService.getInstance(); + TypeFactoryService.getInstance(); + UriLoaderService.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); + // 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); + } + + // --- 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(getCollectionOfIndexes(), uri -> openURI(uri)); + } + + protected void onOpenTypeHierarchy() { + if (currentPage instanceof FocusedTypeGettable) { + FocusedTypeGettable ftg = (FocusedTypeGettable)currentPage; + openTypeHierarchyController.show(getCollectionOfIndexes(), 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(getCollectionOfIndexes(), 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 onWikipedia() { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + if (desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(URI.create("http://en.wikipedia.org/wiki/Java_Decompiler")); + } 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; + checkIndexesChange(page); + checkPreferencesChange(page); + } + + protected void checkIndexesChange(JComponent page) { + if (page instanceof IndexesChangeListener) { + Collection collectionOfIndexes = getCollectionOfIndexes(); + Integer currentHashcode = Integer.valueOf(collectionOfIndexes.hashCode()); + Integer lastHashcode = (Integer)page.getClientProperty("collectionOfIndexes-stamp"); + + if (!currentHashcode.equals(lastHashcode)) { + ((IndexesChangeListener)page).indexesChanged(collectionOfIndexes); + page.putClientProperty("collectionOfIndexes-stamp", currentHashcode); + } + } + } + + 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-stamp"); + + if (!currentHashcode.equals(lastHashcode)) { + ((PreferencesChangeListener)page).preferencesChanged(preferences); + page.putClientProperty("preferences-stamp", 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 collectionOfIndexes = getCollectionOfIndexes(); + for (IndexesChangeListener listener : containerChangeListeners) { + listener.indexesChanged(collectionOfIndexes); + } + if (currentPage instanceof IndexesChangeListener) { + ((IndexesChangeListener)currentPage).indexesChanged(collectionOfIndexes); + } + }); + } + + // --- API --- // + @Override + @SuppressWarnings("unchecked") + public boolean openURI(URI uri) { + if (uri != null) { + boolean success = mainView.openUri(uri); + 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, getCollectionOfIndexes(), entry, query, fragment)); + } else { + // Multiple entries -> Open a "Select location" popup + Collection collectionOfIndexes = getCollectionOfIndexes(); + selectLocationController.show( + new Point(x+(16+2), y+2), + entries, + entry -> openURI(UriUtil.createURI(this, collectionOfIndexes, 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(() -> ((ContentIndexable)component).index(this)); + Indexes indexes = new Indexes() { + @Override public void waitIndexers() { + try { + futureIndexes.get(); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } + } + @Override public Map getIndex(String name) { + try { + return futureIndexes.get().getIndex(name); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + return null; + } + } + }; + + component.putClientProperty("indexes", indexes); + + SwingUtil.invokeLater(() -> { + // Fire 'indexesChanged' event + Collection collectionOfIndexes = getCollectionOfIndexes(); + for (IndexesChangeListener listener : containerChangeListeners) { + listener.indexesChanged(collectionOfIndexes); + } + if (currentPage instanceof IndexesChangeListener) { + ((IndexesChangeListener)currentPage).indexesChanged(collectionOfIndexes); + } + }); + } + + checkIndexesChange(currentPage); + } + + @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(); } + + @SuppressWarnings("unchecked") + public Collection getCollectionOfIndexes() { + List mainPanels = mainView.getMainPanels(); + ArrayList list = new ArrayList<>(mainPanels.size()); + + for (JComponent panel : mainPanels) { + Indexes indexes = (Indexes)panel.getClientProperty("indexes"); + + if (indexes != null) { + list.add(indexes); + } + } + + return list; + } +} diff --git a/app/src/main/java/org/jd/gui/controller/OpenTypeController.java b/app/src/main/java/org/jd/gui/controller/OpenTypeController.java new file mode 100644 index 00000000..39beacb5 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/OpenTypeController.java @@ -0,0 +1,275 @@ +/* + * 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.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 collectionOfIndexes; + 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 collectionOfIndexes, Consumer openCallback) { + // Init attributes + this.collectionOfIndexes = collectionOfIndexes; + this.openCallback = openCallback; + // Refresh view + long hashCode = collectionOfIndexes.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(); + + for (Indexes indexes : collectionOfIndexes) { + indexes.waitIndexers(); + } + + Pattern regExpPattern = createRegExpPattern(pattern); + Map> result = new HashMap<>(); + + for (Indexes indexes : collectionOfIndexes) { + 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()); + } + } + } + } + + 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, collectionOfIndexes, 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, collectionOfIndexes, entry, null, typeName)), // entry selected callback + () -> openTypeView.focus()); // popup close callback + } + } + + // --- IndexesChangeListener --- // + public void indexesChanged(Collection collectionOfIndexes) { + if (openTypeView.isVisible()) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // And refresh + updateList(openTypeView.getPattern()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java b/app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java new file mode 100644 index 00000000..c2d954b1 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java @@ -0,0 +1,85 @@ +/* + * 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.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 collectionOfIndexes; + 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 collectionOfIndexes, Container.Entry entry, String typeName, Consumer openCallback) { + // Init attributes + this.collectionOfIndexes = collectionOfIndexes; + this.openCallback = openCallback; + executor.execute(() -> { + // Waiting the end of indexation... + openTypeHierarchyView.showWaitCursor(); + for (Indexes indexes : collectionOfIndexes) { + indexes.waitIndexers(); + } + SwingUtilities.invokeLater(() -> { + openTypeHierarchyView.hideWaitCursor(); + // Show + openTypeHierarchyView.show(collectionOfIndexes, 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, collectionOfIndexes, 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, collectionOfIndexes, entry, null, typeName)), // entry selected + () -> openTypeHierarchyView.focus()); // popup closeClosure + } + } + + // --- IndexesChangeListener --- // + public void indexesChanged(Collection collectionOfIndexes) { + if (openTypeHierarchyView.isVisible()) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // And refresh + openTypeHierarchyView.updateTree(collectionOfIndexes); + } + } +} diff --git a/app/src/main/java/org/jd/gui/controller/PreferencesController.java b/app/src/main/java/org/jd/gui/controller/PreferencesController.java new file mode 100644 index 00000000..1d3fbec7 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/PreferencesController.java @@ -0,0 +1,29 @@ +/* + * 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); + } +} diff --git a/app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java b/app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java new file mode 100644 index 00000000..13b77dd1 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java @@ -0,0 +1,94 @@ +/* + * 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.io.IOException; +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 (IOException e) { + assert ExceptionUtil.printStackTrace(e); + } + + 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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java b/app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java new file mode 100644 index 00000000..10d6b636 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java @@ -0,0 +1,466 @@ +/* + * 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.FilteredContainerWrapper; +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.awt.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +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 filteredContainerWrappers = new HashSet<>(); + protected Collection collectionOfIndexes; + 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 collectionOfIndexes, Consumer openCallback) { + // Init attributes + this.collectionOfIndexes = collectionOfIndexes; + this.openCallback = openCallback; + // Refresh view + long hashCode = collectionOfIndexes.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) { + filteredContainerWrappers.clear(); + + executor.execute(() -> { + searchInConstantPoolsView.showWaitCursor(); + + int matchingTypeCount = 0; + int patternLength = pattern.length(); + + if (patternLength > 0) { + // Waiting the end of indexation... + for (Indexes indexes : collectionOfIndexes) { + indexes.waitIndexers(); + } + + for (Indexes indexes : collectionOfIndexes) { + 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 + filteredContainerWrappers.add(new FilteredContainerWrapper(container, matchingEntries)); + } + } + } + + final int count = matchingTypeCount; + + searchInConstantPoolsView.hideWaitCursor(); + searchInConstantPoolsView.updateTree(filteredContainerWrappers, 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_TYPE_DECLARATION) != 0); + boolean references = ((flags & SearchInConstantPoolsView.SEARCH_TYPE_REFERENCE) != 0); + + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_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_TYPE_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_TYPE_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_TYPE_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_TYPE_STRING) != 0) { + if (declarations || references) + match(indexes, "strings", 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 matchedTypes = cache.get(key); + + if (matchedTypes == null) { + Map index = indexes.getIndex(indexName); + + if (patternLength == 1) { + matchedTypes = matchWithCharFunction.apply(pattern.charAt(0), index); + } else { + String lastKey = key.substring(0, key.length() - 1); + Map lastMatchedTypes = cache.get(lastKey); + if (lastMatchedTypes != null) { + matchedTypes = matchWithStringFunction.apply(pattern, lastMatchedTypes); + } else { + matchedTypes = matchWithStringFunction.apply(pattern, index); + } + } + + // Cache matchingEntries + cache.put(key, matchedTypes); + } + + if (matchedTypes != null) { + for (Collection entries : matchedTypes.values()) { + matchingEntries.addAll(entries); + } + } + } + } + + protected static Map matchTypeEntriesWithChar(char c, Map index) { + if ((c == '*') || (c == '?')) { + return Collections.emptyMap(); + } 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 Collections.emptyMap(); + } else { + Map map = new HashMap<>(); + + for (String typeName : index.keySet()) { + if (!typeName.isEmpty() && (typeName.charAt(0) == c)) { + map.put(typeName, index.get(typeName)); + } + } + + return map; + } + } + + protected static Map matchWithString(String pattern, Map index) { + Pattern p = createPattern(pattern); + Map map = new HashMap<>(); + + for (String typeName : index.keySet()) { + if (p.matcher(typeName).matches()) { + map.put(typeName, index.get(typeName)); + } + } + + 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 (FilteredContainerWrapper container : filteredContainerWrappers) { + 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_TYPE_DECLARATION) != 0) + sbPattern.append('d'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_REFERENCE) != 0) + sbPattern.append('r'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_TYPE) != 0) + sbPattern.append('t'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_CONSTRUCTOR) != 0) + sbPattern.append('c'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_METHOD) != 0) + sbPattern.append('m'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_FIELD) != 0) + sbPattern.append('f'); + if ((flags & SearchInConstantPoolsView.SEARCH_TYPE_STRING) != 0) + sbPattern.append('s'); + + // 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(collectionOfIndexes, 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 collectionOfIndexes) { + if (searchInConstantPoolsView.isVisible()) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // And refresh + updateTree(searchInConstantPoolsView.getPattern(), searchInConstantPoolsView.getFlags()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/controller/SelectLocationController.java b/app/src/main/java/org/jd/gui/controller/SelectLocationController.java new file mode 100644 index 00000000..fe9106b0 --- /dev/null +++ b/app/src/main/java/org/jd/gui/controller/SelectLocationController.java @@ -0,0 +1,181 @@ +/* + * 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.FilteredContainerWrapper; +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 filteredContainerWrappers = 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 + filteredContainerWrappers.add(new FilteredContainerWrapper(container, getOuterEntries(mapEntry.getValue()))); + } + + selectLocationView.show( + location, filteredContainerWrappers, entries.size(), + new Consumer() { + @Override public void accept(URI uri) { onLocationSelected(filteredContainerWrappers, uri, selectedLocationCallback); } + }, + 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 filteredContainerWrappers, URI uri, Consumer selectedLocationCallback) { + // Open the single entry uri + Container.Entry entry = null; + + for (FilteredContainerWrapper container : filteredContainerWrappers) { + 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()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/model/configuration/Configuration.java b/app/src/main/java/org/jd/gui/model/configuration/Configuration.java new file mode 100644 index 00000000..e661fb65 --- /dev/null +++ b/app/src/main/java/org/jd/gui/model/configuration/Configuration.java @@ -0,0 +1,103 @@ +/* + * 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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/model/container/FilteredContainerWrapper.java b/app/src/main/java/org/jd/gui/model/container/FilteredContainerWrapper.java new file mode 100644 index 00000000..ea5076ab --- /dev/null +++ b/app/src/main/java/org/jd/gui/model/container/FilteredContainerWrapper.java @@ -0,0 +1,114 @@ +/* + * 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 FilteredContainerWrapper implements Container { + protected Container container; + protected EntryWrapper root; + + protected Set validEntries = new HashSet<>(); + protected Map uriToEntryWrapper = new HashMap<>(); + protected Map uriToContainerWrapper = new HashMap<>(); + + public FilteredContainerWrapper(Container container, Collection entries) { + this.container = container; + this.root = getEntryWrapper(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 uriToEntryWrapper.get(uri); } + public Set getUris() { return validEntries; } + + protected EntryWrapper getEntryWrapper(Container.Entry entry) { + URI uri = entry.getUri(); + EntryWrapper entryWrapper = uriToEntryWrapper.get(uri); + if (entryWrapper == null) { + uriToEntryWrapper.put(uri, entryWrapper=new EntryWrapper(entry)); + } + return entryWrapper; + } + + protected ContainerWrapper getContainerWrapper(Container.Entry entry) { + URI uri = entry.getContainer().getRoot().getUri(); + ContainerWrapper containerWrapper = uriToContainerWrapper.get(uri); + if (containerWrapper == null) { + uriToContainerWrapper.put(uri, containerWrapper=new ContainerWrapper(entry.getContainer())); + } + return containerWrapper; + } + + protected class EntryWrapper implements Entry, Comparable { + protected Entry entry; + protected Collection children; + + public EntryWrapper(Entry entry) { + this.entry = entry; + } + + @Override public Container getContainer() { return getContainerWrapper(entry.getContainer().getRoot()); } + @Override public Entry getParent() { return getEntryWrapper(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(getEntryWrapper(child)); + } + } + } + return children; + } + + @Override + public int compareTo(EntryWrapper other) { + if (entry.isDirectory()) { + if (!other.isDirectory()) { + return -1; + } + } else { + if (other.isDirectory()) { + return 1; + } + } + return entry.getPath().compareTo(other.getPath()); + } + } + + protected class ContainerWrapper implements Container { + protected Container container; + + public ContainerWrapper(Container container) { + this.container = container; + } + + @Override public String getType() { return container.getType(); } + @Override public Entry getRoot() { return getEntryWrapper(container.getRoot()); } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/jd/gui/model/history/History.java b/app/src/main/java/org/jd/gui/model/history/History.java new file mode 100644 index 00000000..05c1b438 --- /dev/null +++ b/app/src/main/java/org/jd/gui/model/history/History.java @@ -0,0 +1,83 @@ +/* + * 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(); } +} diff --git a/app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java b/app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java new file mode 100644 index 00000000..c47b6671 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java @@ -0,0 +1,85 @@ +/* + * 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); + } + } +} diff --git a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersister.groovy b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersister.java similarity index 69% rename from app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersister.groovy rename to app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersister.java index 2bbf0241..40c1dcd9 100644 --- a/app/src/main/groovy/org/jd/gui/service/configuration/ConfigurationPersister.groovy +++ b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersister.java @@ -5,11 +5,11 @@ * copy and modify the code freely for non-commercial purposes. */ -package org.jd.gui.service.configuration +package org.jd.gui.service.configuration; -import org.jd.gui.model.configuration.Configuration +import org.jd.gui.model.configuration.Configuration; -interface ConfigurationPersister { +public interface ConfigurationPersister { Configuration load(); void save(Configuration configuration); diff --git a/app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java new file mode 100644 index 00000000..fb4ba710 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java @@ -0,0 +1,22 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java new file mode 100644 index 00000000..51bf957c --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java @@ -0,0 +1,276 @@ +/* + * 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.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +public class ConfigurationXmlPersisterProvider implements ConfigurationPersister { + protected static final String ERROR_BACKGROUND_COLOR = "JdGuiPreferences.errorBackgroundColor"; + + protected static final File FILE = getConfigFile(); + + protected static File getConfigFile() { + 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); + } + + 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); + + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLStreamReader reader = null; + + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(FILE))) { + // Load values + reader = factory.createXMLStreamReader(bis); + + 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); + } + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (XMLStreamException e) { + assert ExceptionUtil.printStackTrace(e); + } + } + } + + if (! config.getPreferences().containsKey(ERROR_BACKGROUND_COLOR)) { + config.getPreferences().put(ERROR_BACKGROUND_COLOR, "0xFF6666"); + } + + return config; + } + + public void save(Configuration configuration) { + Point l = configuration.getMainWindowLocation(); + Dimension s = configuration.getMainWindowSize(); + + XMLOutputFactory factory = XMLOutputFactory.newInstance(); + XMLStreamWriter writer = null; + + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(FILE))) { + // Load values + writer = factory.createXMLStreamWriter(bos); + 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(); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (XMLStreamException e) { + assert ExceptionUtil.printStackTrace(e); + } + } + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java b/app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java new file mode 100644 index 00000000..31cae51a --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java @@ -0,0 +1,33 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/service/extension/ExtensionService.java b/app/src/main/java/org/jd/gui/service/extension/ExtensionService.java new file mode 100644 index 00000000..cc864812 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/extension/ExtensionService.java @@ -0,0 +1,85 @@ +/* + * 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 = (URL[])urls.toArray(); + Arrays.sort(array, URL_COMPARATOR); + extensionClassLoader = new URLClassLoader(array, ExtensionService.class.getClassLoader()); + } + } + } catch (Exception e) { + 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()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java b/app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java new file mode 100644 index 00000000..148d30ce --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java @@ -0,0 +1,51 @@ +/* + * 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); + + if ((provider != null) && provider.accept(api, file)) { + return provider; + } + + return null; + } + + public HashMap getMapProviders() { + return mapProviders; + } +} diff --git a/app/src/main/java/org/jd/gui/service/indexer/IndexerService.java b/app/src/main/java/org/jd/gui/service/indexer/IndexerService.java new file mode 100644 index 00000000..30374a03 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/indexer/IndexerService.java @@ -0,0 +1,110 @@ +/* + * 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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java b/app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java new file mode 100644 index 00000000..ca9ddfe7 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java @@ -0,0 +1,194 @@ +/* + * 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().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<>(); + MapMapCollectionWithDefault mapWithDefault = new MapMapCollectionWithDefault(map); + + // Index populating value automatically + Indexes indexesWithDefault = new Indexes() { + @Override public void waitIndexers() {} + @Override public Map getIndex(String name) { return 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 new Indexes() { + @Override public void waitIndexers() {} + @Override public Map getIndex(String name) { return 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 MapWrapper implements Map { + protected Map map; + + public MapWrapper(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 MapCollectionWithDefault extends MapWrapper { + public MapCollectionWithDefault(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 MapMapCollectionWithDefault extends MapWrapper> { + protected HashMap> wrappers = new HashMap<>(); + + public MapMapCollectionWithDefault(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 MapCollectionWithDefault(m)); + } + + return value; + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java b/app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java new file mode 100644 index 00000000..0108ce1e --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java @@ -0,0 +1,38 @@ +/* + * 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"); + } +} diff --git a/app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java b/app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java new file mode 100644 index 00000000..c68ea29c --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java @@ -0,0 +1,30 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/service/platform/PlatformService.java b/app/src/main/java/org/jd/gui/service/platform/PlatformService.java new file mode 100644 index 00000000..51ea1aff --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/platform/PlatformService.java @@ -0,0 +1,36 @@ +/* + * 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; } +} diff --git a/app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java b/app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java new file mode 100644 index 00000000..0ee9a6d8 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java @@ -0,0 +1,46 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java b/app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java new file mode 100644 index 00000000..8d195d49 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java @@ -0,0 +1,55 @@ +/* + * 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) {} +} diff --git a/app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java b/app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java new file mode 100644 index 00000000..3e5022ea --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java @@ -0,0 +1,54 @@ +/* + * 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) {} +} diff --git a/app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java b/app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java new file mode 100644 index 00000000..4e56a20e --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java @@ -0,0 +1,110 @@ +/* + * 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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java b/app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java new file mode 100644 index 00000000..ffde27b7 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java @@ -0,0 +1,110 @@ +/* + * 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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java b/app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java new file mode 100644 index 00000000..c0215efe --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java @@ -0,0 +1,122 @@ +/* + * 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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java b/app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java new file mode 100644 index 00000000..980e8626 --- /dev/null +++ b/app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java @@ -0,0 +1,44 @@ +/* + * 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; + } + } +} diff --git a/services/src/main/groovy/org/jd/gui/view/data/TreeNodeBean.groovy b/app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java similarity index 54% rename from services/src/main/groovy/org/jd/gui/view/data/TreeNodeBean.groovy rename to app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java index a3dd410f..d9316865 100644 --- a/services/src/main/groovy/org/jd/gui/view/data/TreeNodeBean.groovy +++ b/app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java @@ -5,15 +5,11 @@ * copy and modify the code freely for non-commercial purposes. */ -package org.jd.gui.view.data +package org.jd.gui.util.exception; -import org.jd.gui.api.model.TreeNodeData - -import javax.swing.* - -class TreeNodeBean implements TreeNodeData { - String label - String tip - Icon icon - Icon openIcon +public class ExceptionUtil { + public static boolean printStackTrace(Throwable throwable) { + throwable.printStackTrace(); + return true; + } } diff --git a/app/src/main/java/org/jd/gui/util/function/TriConsumer.java b/app/src/main/java/org/jd/gui/util/function/TriConsumer.java new file mode 100644 index 00000000..f731aba0 --- /dev/null +++ b/app/src/main/java/org/jd/gui/util/function/TriConsumer.java @@ -0,0 +1,17 @@ +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); + }; + } +} diff --git a/app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java b/app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java new file mode 100644 index 00000000..1ef55b9d --- /dev/null +++ b/app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java @@ -0,0 +1,54 @@ +/* + * 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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/util/net/UriUtil.java b/app/src/main/java/org/jd/gui/util/net/UriUtil.java new file mode 100644 index 00000000..6be1fcd2 --- /dev/null +++ b/app/src/main/java/org/jd/gui/util/net/UriUtil.java @@ -0,0 +1,68 @@ +/* + * 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; + +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 collectionOfIndexes, Container.Entry entry, String query, String fragment) { + TypeFactory typeFactory = TypeFactoryService.getInstance().get(entry); + + if (typeFactory != null) { + Type type = typeFactory.make(api, entry, fragment); + + if (type != null) { + URI uri = entry.getUri(); + String path = getOuterPath(collectionOfIndexes, entry, type); + + try { + return new URI(uri.getScheme(), uri.getHost(), path, query, fragment); + } catch (URISyntaxException e) { + ExceptionUtil.printStackTrace(e); + } + } + } + + return null; + } + + @SuppressWarnings("unchecked") + protected static String getOuterPath(Collection collectionOfIndexes, Container.Entry entry, Type type) { + String outerName = type.getOuterName(); + + if (outerName != null) { + for (Indexes indexes : collectionOfIndexes) { + Collection outerEntries = indexes.getIndex("typeDeclarations").get(outerName); + + if (outerEntries != null) { + for (Container.Entry outerEntry : outerEntries) { + if (outerEntry.getContainer() == entry.getContainer()) { + return outerEntry.getUri().getPath(); + } + } + } + } + } + + return entry.getUri().getPath(); + } +} diff --git a/app/src/main/groovy/org/jd/gui/util/swing/SwingUtil.groovy b/app/src/main/java/org/jd/gui/util/swing/SwingUtil.java similarity index 70% rename from app/src/main/groovy/org/jd/gui/util/swing/SwingUtil.groovy rename to app/src/main/java/org/jd/gui/util/swing/SwingUtil.java index e1161c51..51914c12 100644 --- a/app/src/main/groovy/org/jd/gui/util/swing/SwingUtil.groovy +++ b/app/src/main/java/org/jd/gui/util/swing/SwingUtil.java @@ -5,23 +5,22 @@ * copy and modify the code freely for non-commercial purposes. */ -package org.jd.gui.util.swing +package org.jd.gui.util.swing; -import groovy.transform.CompileStatic +import org.jd.gui.util.exception.ExceptionUtil; +import org.jd.gui.view.MainView; -import javax.swing.JComponent -import javax.swing.JPopupMenu -import javax.swing.JSeparator -import javax.swing.LookAndFeel -import javax.swing.UIManager -import java.lang.reflect.Field -import java.lang.reflect.Method +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 */ -@CompileStatic -class SwingUtil { +public class SwingUtil { /* * This is free and unencumbered software released into the public domain. * @@ -84,6 +83,7 @@ public static void installGtkPopupBugWorkaround() { fixGtkThickness(style, "yThickness"); } catch (Exception e) { // Silently ignored. Workaround can't be applied. + assert ExceptionUtil.printStackTrace(e); } } @@ -135,4 +135,55 @@ private static Object getGtkStyle(Object styleFactory, JComponent component, Str 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; + } } diff --git a/app/src/main/java/org/jd/gui/view/AboutView.java b/app/src/main/java/org/jd/gui/view/AboutView.java new file mode 100644 index 00000000..0d49f250 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/AboutView.java @@ -0,0 +1,146 @@ +/* + * 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 javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +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(new EmptyBorder(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(new LineBorder(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(new EmptyBorder(15, 15, 15, 15)); + logo.setBackground(Color.GRAY); + logo.setOpaque(true); + subpanel.add(logo, BorderLayout.WEST); + Box subvbox = Box.createVerticalBox(); + subvbox.setBorder(new EmptyBorder(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(new EmptyBorder(5,10,5,5)); + subsubpanel.setBackground(Color.YELLOW); + subsubpanel.setOpaque(true); + + 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()); + hbox.setBackground(Color.RED); + hbox.setOpaque(true); + subvbox.add(hbox); + subvbox.setBackground(Color.GREEN); + subvbox.setOpaque(true); + + 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(); + aboutDialog.setLocationRelativeTo(mainFrame); + }); + } + + public void show() { + SwingUtil.invokeLater(() -> { + // Show + aboutDialog.setVisible(true); + aboutOkButton.requestFocus(); + }); + } +} diff --git a/app/src/main/java/org/jd/gui/view/GoToView.java b/app/src/main/java/org/jd/gui/view/GoToView.java new file mode 100644 index 00000000..4c8e5d9b --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/GoToView.java @@ -0,0 +1,164 @@ +/* + * 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.border.EmptyBorder; +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(new EmptyBorder(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(); + }); + } +} diff --git a/app/src/main/java/org/jd/gui/view/MainView.java b/app/src/main/java/org/jd/gui/view/MainView.java new file mode 100644 index 00000000..8e5fb951 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/MainView.java @@ -0,0 +1,452 @@ +/* + * 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.border.EmptyBorder; +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.*; +import java.io.*; +import java.net.URI; +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 wikipediaActionListener, + 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.setIconImage(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(new EmptyBorder(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 wikipediaAction = newAction("Wikipedia", browser, "Open Wikipedia page", wikipediaActionListener); + 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(wikipediaAction); + 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); + } +} diff --git a/app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java b/app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java new file mode 100644 index 00000000..dd310456 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java @@ -0,0 +1,451 @@ +/* + * 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.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.border.EmptyBorder; +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; + +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 collectionOfIndexes; + + 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(new EmptyBorder(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 collectionOfIndexes, Container.Entry entry, String typeName) { + this.collectionOfIndexes = collectionOfIndexes; + 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 collectionOfIndexes) { + this.collectionOfIndexes = collectionOfIndexes; + 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<>(); + + for (Indexes indexes : collectionOfIndexes) { + Map subTypeNames = indexes.getIndex("subTypeNames"); + if (subTypeNames != null) { + Collection collection = subTypeNames.get(typeName); + if (collection != null) { + for (String tn : collection) { + if (tn != null) { + result.add(tn); + } + } + } + } + } + + return result; + } + + @SuppressWarnings("unchecked") + protected List getEntries(String typeName) { + ArrayList result = new ArrayList<>(); + + for (Indexes indexes : collectionOfIndexes) { + Map typeDeclarations = indexes.getIndex("typeDeclarations"); + if (typeDeclarations != null) { + Collection collection = typeDeclarations.get(typeName); + if (collection != null) { + for (Container.Entry e : collection) { + if (e != null) { + result.add(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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/OpenTypeView.java b/app/src/main/java/org/jd/gui/view/OpenTypeView.java new file mode 100644 index 00000000..332c4879 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/OpenTypeView.java @@ -0,0 +1,266 @@ +/* + * 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.border.EmptyBorder; +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.*; +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(new EmptyBorder(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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/PreferencesView.java b/app/src/main/java/org/jd/gui/view/PreferencesView.java new file mode 100644 index 00000000..16badcf4 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/PreferencesView.java @@ -0,0 +1,193 @@ +/* + * 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 javax.swing.border.EmptyBorder; +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(new EmptyBorder(15, 15, 15, 15)); + panel.setLayout(new BorderLayout()); + preferencesDialog.add(panel); + + // Box for preferences panels + Box preferencesPanels = Box.createVerticalBox(); + 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(new EmptyBorder(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()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java b/app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java new file mode 100644 index 00000000..be24077d --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java @@ -0,0 +1,122 @@ +/* + * 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 javax.swing.border.EmptyBorder; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.util.function.IntConsumer; + +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(new EmptyBorder(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(); + saveAllSourcesDialog.setLocationRelativeTo(mainFrame); + }); + } + + 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.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); + }); + } +} diff --git a/app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java b/app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java new file mode 100644 index 00000000..9f12d662 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java @@ -0,0 +1,430 @@ +/* + * 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.FilteredContainerWrapper; +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.border.EmptyBorder; +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_TYPE = 1; + public static final int SEARCH_TYPE_CONSTRUCTOR = 2; + public static final int SEARCH_TYPE_METHOD = 4; + public static final int SEARCH_TYPE_FIELD = 8; + public static final int SEARCH_TYPE_STRING = 16; + public static final int SEARCH_TYPE_DECLARATION = 32; + public static final int SEARCH_TYPE_REFERENCE = 64; + + 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 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(new EmptyBorder(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.getKeyChar() == 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); + 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.getKeyChar() == 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()); + } + searchInConstantPoolsDialog.setVisible(false); + } + }; + 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_TYPE; + if (searchInConstantPoolsCheckBoxConstructor.isSelected()) + flags += SEARCH_TYPE_CONSTRUCTOR; + if (searchInConstantPoolsCheckBoxMethod.isSelected()) + flags += SEARCH_TYPE_METHOD; + if (searchInConstantPoolsCheckBoxField.isSelected()) + flags += SEARCH_TYPE_FIELD; + if (searchInConstantPoolsCheckBoxString.isSelected()) + flags += SEARCH_TYPE_STRING; + if (searchInConstantPoolsCheckBoxDeclarations.isSelected()) + flags += SEARCH_TYPE_DECLARATION; + if (searchInConstantPoolsCheckBoxReferences.isSelected()) + flags += SEARCH_TYPE_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 (FilteredContainerWrapper 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()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/SelectLocationView.java b/app/src/main/java/org/jd/gui/view/SelectLocationView.java new file mode 100644 index 00000000..c87bd622 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/SelectLocationView.java @@ -0,0 +1,202 @@ +/* + * 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.FilteredContainerWrapper; +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.border.EmptyBorder; +import javax.swing.border.LineBorder; +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.Consumer; + +public class SelectLocationView { + protected static final FilteredContainerWrapperComparator FILTERED_CONTAINER_WRAPPER_COMPARATOR = new FilteredContainerWrapperComparator(); + + 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(new LineBorder(bg.darker())); + selectLocationPanel.setBackground(bg); + selectLocationDialog.add(selectLocationPanel); + + selectLocationLabel = new JLabel(); + selectLocationLabel.setBorder(new EmptyBorder(5, 5, 0, 5)); + selectLocationPanel.add(selectLocationLabel, BorderLayout.NORTH); + + selectLocationTree = new Tree(); + selectLocationTree.setBorder(new EmptyBorder(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(FILTERED_CONTAINER_WRAPPER_COMPARATOR); + + for (FilteredContainerWrapper 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 FilteredContainerWrapperComparator implements Comparator { + @Override + public int compare(FilteredContainerWrapper fcw1, FilteredContainerWrapper fcw2) { + return fcw1.getRoot().getUri().compareTo(fcw2.getRoot().getUri()); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java b/app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java new file mode 100644 index 00000000..36fa222c --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java @@ -0,0 +1,35 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/IconButton.java b/app/src/main/java/org/jd/gui/view/component/IconButton.java new file mode 100644 index 00000000..9bed06ff --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/IconButton.java @@ -0,0 +1,27 @@ +/* + * 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); + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/List.java b/app/src/main/java/org/jd/gui/view/component/List.java new file mode 100644 index 00000000..afd72ed6 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/List.java @@ -0,0 +1,90 @@ +/* + * 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; + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/Tree.java b/app/src/main/java/org/jd/gui/view/component/Tree.java new file mode 100644 index 00000000..bbfaa5cc --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/Tree.java @@ -0,0 +1,34 @@ +/* + * 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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java b/app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java new file mode 100644 index 00000000..e96af70e --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java @@ -0,0 +1,197 @@ +/* + * 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 javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +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 (!uri.equals(page.getUri()) && (page instanceof UriOpenable)) { + // Enable page changed event + pageChangedListenersEnabled = true; + // Search & display sub tab + return ((UriOpenable)page).openUri(uri); + } + return true; + } else { + return false; + } + } finally { + // Enable page changed event + pageChangedListenersEnabled = true; + } + } + + // --- 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); + } + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java b/app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java new file mode 100644 index 00000000..6907bb0a --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java @@ -0,0 +1,260 @@ +/* + * 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) { + // Update preferences + if (page instanceof PreferencesChangeListener) { + ((PreferencesChangeListener)page).preferencesChanged(preferences); + } + // 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(TabbedPanel.CLOSE_ACTIVE_ICON); } + @Override public void mouseExited(MouseEvent e) { ((JLabel)e.getSource()).setIcon(TabbedPanel.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.toString(); + int i = tabbedPane.getTabCount(); + + while (i-- > 0) { + T page = (T)tabbedPane.getComponentAt(i); + String u2 = page.getUri().toString(); + 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()); + } +} diff --git a/app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java b/app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java new file mode 100644 index 00000000..36749e0b --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java @@ -0,0 +1,321 @@ +/* + * 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.*; +import javax.swing.tree.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; + +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()) { + Object element = enumeration.nextElement(); + + hashCode *= 31; + + 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); + page.putClientProperty("preferences-stamp", Integer.valueOf(api.getPreferences().hashCode())); + page.putClientProperty("collectionOfIndexes-stamp", Integer.valueOf(api.getCollectionOfIndexes().hashCode())); + + 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); + DefaultMutableTreeNode baseNode = searchTreeNode(baseUri, (DefaultMutableTreeNode)tree.getModel().getRoot()); + + if ((baseNode != null) && showPage(uri, baseUri, baseNode)) { + DefaultMutableTreeNode node = searchTreeNode(uri, baseNode); + + if (node != null) { + try { + // Disable tree node changed listener + treeNodeChangedEnabled = false; + // 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 = 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); + } +} diff --git a/app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java b/app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java new file mode 100644 index 00000000..960e81dc --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java @@ -0,0 +1,84 @@ +/* + * 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; + } +} diff --git a/app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java b/app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java new file mode 100644 index 00000000..5fbd3b19 --- /dev/null +++ b/app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java @@ -0,0 +1,84 @@ +/* + * 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; + } +} diff --git a/build.gradle b/build.gradle index 6ffc8ee2..51658b78 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.netflix.nebula:gradle-ospackage-plugin:5.3.0' - classpath 'org.spockframework:spock-core:1.2-groovy-2.5' + classpath 'com.netflix.nebula:gradle-ospackage-plugin:5.3.0' // RPM & DEB support classpath 'edu.sc.seis.gradle:launch4j:2.4.4' } } @@ -26,10 +25,7 @@ allprojects { tasks.withType(JavaCompile) { sourceCompatibility = targetCompatibility = '1.8' options.compilerArgs << '-Xlint:unchecked' - } - tasks.withType(GroovyCompile) { - sourceCompatibility = targetCompatibility = '1.8' - options.compilerArgs << '-Xlint:unchecked' + options.encoding = 'UTF-8' } repositories { @@ -59,13 +55,6 @@ subprojects.each { subproject -> jar { dependsOn subprojects.tasks['jar'] - // Add SPI directory - def tmpSpiDir = file('build/tmp/spi') - tmpSpiDir.deleteDir() - def tmpSpiServicesDir = file(tmpSpiDir.path + '/META-INF/services') - tmpSpiServicesDir.mkdirs() - from tmpSpiDir - // Add dependencies def deps = [] subprojects.each { subproject -> @@ -85,9 +74,16 @@ jar { 'JD-GUI-Version': project.version, 'JD-Core-Version': '0.7.1' } - exclude 'META-INF/groovy/**', 'META-INF/licenses/**', 'META-INF/maven/**', 'META-INF/INDEX.LIST' + exclude 'META-INF/licenses/**', 'META-INF/maven/**', 'META-INF/INDEX.LIST' duplicatesStrategy DuplicatesStrategy.EXCLUDE doFirst { + // Add SPI directory + def tmpSpiDir = file('build/tmp/spi') + tmpSpiDir.deleteDir() + def tmpSpiServicesDir = file(tmpSpiDir.path + '/META-INF/services') + tmpSpiServicesDir.mkdirs() + from tmpSpiDir + // Copy and merge SPI config files subprojects.each { subproject -> def servicesDir = file(subproject.sourceSets.main.output.resourcesDir.path + '/META-INF/services') diff --git a/services/build.gradle b/services/build.gradle index bcf8a451..8550852a 100644 --- a/services/build.gradle +++ b/services/build.gradle @@ -1,14 +1,11 @@ apply plugin: 'java' -apply plugin: 'groovy' dependencies { - compile 'org.codehaus.groovy:groovy:2.5.6' - compile 'org.codehaus.groovy:groovy-swing:2.5.6' - compile 'org.codehaus.groovy:groovy-xml:2.5.6' compile 'com.fifesoft:rsyntaxtextarea:2.5.6' + compile 'org.ow2.asm:asm:7.1' compile 'jd:jd-core:0.7.1' compile project(':api') - testCompile 'org.codehaus.groovy:groovy-test:2.5.6' + testCompile 'junit:junit:4.12' } version = parent.version diff --git a/services/src/main/groovy/org/jd/gui/model/container/EarContainer.groovy b/services/src/main/groovy/org/jd/gui/model/container/EarContainer.groovy deleted file mode 100644 index 66f0d31f..00000000 --- a/services/src/main/groovy/org/jd/gui/model/container/EarContainer.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container - -import java.nio.file.Path - -@CompileStatic -class EarContainer extends GenericContainer { - - EarContainer(API api, Container.Entry parentEntry, Path rootPath) { - super(api, parentEntry, rootPath) - } - - String getType() { 'ear' } -} diff --git a/services/src/main/groovy/org/jd/gui/model/container/GenericContainer.groovy b/services/src/main/groovy/org/jd/gui/model/container/GenericContainer.groovy deleted file mode 100644 index f5295987..00000000 --- a/services/src/main/groovy/org/jd/gui/model/container/GenericContainer.groovy +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container - -import java.nio.file.* - -@CompileStatic -class GenericContainer implements Container { - protected API api - protected int rootNameCount - protected Container.Entry root - - GenericContainer(API api, Container.Entry parentEntry, Path rootPath) { - this.api = api - this.rootNameCount = rootPath.nameCount - this.root = new Entry(parentEntry, rootPath, parentEntry.uri) { - Entry newChildEntry(Path fsPath) { new Entry(parent, fsPath, null) } - } - } - - String getType() { 'generic' } - Container.Entry getRoot() { root } - - class Entry implements Container.Entry, Comparable { - protected Container.Entry parent - protected Path fsPath - protected String strPath - protected URI uri - protected Boolean isDirectory - protected Collection children - - 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 - } - - Entry newChildEntry(Path fsPath) { new Entry(this, fsPath, null) } - - Container getContainer() { GenericContainer.this } - Container.Entry getParent() { parent } - - URI getUri() { - if (uri == null) { - uri = new URI(root.uri.scheme, root.uri.host, root.uri.path + '!/' + path, null) - } - return uri - } - - String getPath() { - if (strPath == null) { - if (rootNameCount == fsPath.nameCount) { - strPath = '' - } else { - strPath = fsPath.subpath(rootNameCount, fsPath.nameCount).join('/') - if (strPath.endsWith(fsPath.fileSystem.separator)) { - // Cut last separator - strPath = strPath.substring(0, strPath.length()-fsPath.fileSystem.separator.length()) - } - } - } - return strPath - } - - boolean isDirectory() { - if (isDirectory == null) { - isDirectory = Boolean.valueOf(Files.isDirectory(fsPath)) - } - return isDirectory - } - - long length() { Files.size(fsPath) } - InputStream getInputStream() { Files.newInputStream(fsPath) } - - Collection getChildren() { - if (children == null) { - if (Files.isDirectory(fsPath)) { - children = loadChildrenFromDirectoryEntry() - } else { - children = loadChildrenFromFileEntry() - } - } - return children - } - - protected Collection loadChildrenFromDirectoryEntry() { - DirectoryStream stream = null - - try { - def children = new ArrayList() - int parentNameCount = fsPath.nameCount - stream = Files.newDirectoryStream(fsPath) - - for (def subPath : stream) { - if (subPath.nameCount > parentNameCount) { - children.add(newChildEntry(subPath)) - } - } - - return Collections.unmodifiableCollection(children.sort()) - } finally { - stream?.close() - } - } - - protected Collection loadChildrenFromFileEntry() { - def tmpFile = File.createTempFile('jd-gui.', '.tmp.' + fsPath.fileName.toString()) - def tmpPath = Paths.get(tmpFile.toURI()) - - tmpFile.withOutputStream { OutputStream os -> - Files.copy(fsPath, os) - } - - FileSystem subFileSystem = FileSystems.newFileSystem(tmpPath, null) - - if (subFileSystem) { - def rootDirectories = subFileSystem.rootDirectories.iterator() - - if (rootDirectories.hasNext()) { - tmpFile.deleteOnExit() - - def rootPath = rootDirectories.next() - def container = api.getContainerFactory(rootPath)?.make(api, this, rootPath) - if (container) { - return container.root.children - } - } - } - - tmpFile.delete() - return Collections.emptyList() - } - - /** - * Directories before files, sorted by path - */ - int compareTo(Entry other) { - if (Files.isDirectory(fsPath)) { - if (!other.isDirectory()) { - return -1 - } - } else { - if (other.isDirectory()) { - return 1 - } - } - return fsPath.compareTo(other.fsPath) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/model/container/JarContainer.groovy b/services/src/main/groovy/org/jd/gui/model/container/JarContainer.groovy deleted file mode 100644 index 420ca146..00000000 --- a/services/src/main/groovy/org/jd/gui/model/container/JarContainer.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container - -import java.nio.file.Path - -@CompileStatic -class JarContainer extends GenericContainer { - - JarContainer(API api, Container.Entry parentEntry, Path rootPath) { - super(api, parentEntry, rootPath) - } - - String getType() { 'jar' } -} diff --git a/services/src/main/groovy/org/jd/gui/model/container/WarContainer.groovy b/services/src/main/groovy/org/jd/gui/model/container/WarContainer.groovy deleted file mode 100644 index d718db09..00000000 --- a/services/src/main/groovy/org/jd/gui/model/container/WarContainer.groovy +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container - -import java.nio.file.Path - -@CompileStatic -class WarContainer extends GenericContainer { - - WarContainer(API api, Container.Entry parentEntry, Path rootPath) { - super(api, parentEntry, rootPath) - } - - String getType() { 'war' } -} diff --git a/services/src/main/groovy/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.groovy b/services/src/main/groovy/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.groovy deleted file mode 100644 index f180a65a..00000000 --- a/services/src/main/groovy/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.groovy +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 com.sun.media.sound.InvalidFormatException -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.spi.ContextualActionsFactory - -import javax.swing.AbstractAction -import javax.swing.Action -import javax.swing.ImageIcon -import java.awt.Toolkit -import java.awt.datatransfer.StringSelection -import java.awt.event.ActionEvent - -class CopyQualifiedNameContextualActionsFactory implements ContextualActionsFactory { - - Collection make(API api, Container.Entry entry, String fragment) { - return Collections.singletonList(new CopyQualifiedNameAction(api, entry, fragment)) - } - - static class CopyQualifiedNameAction extends AbstractAction { - static final ImageIcon ICON = new ImageIcon(CopyQualifiedNameAction.class.classLoader.getResource('org/jd/gui/images/cpyqual_menu.png')) - - protected API api - protected Container.Entry entry - protected String fragment - - 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) - } - - void actionPerformed(ActionEvent e) { - if (fragment) { - def type = api.getTypeFactory(entry)?.make(api, entry, fragment) - - if (type) { - def sb = new StringBuffer(type.displayPackageName) - int dashIndex = fragment.indexOf('-') - - if (sb.length() > 0) { - sb.append('.') - } - - sb.append(type.displayTypeName) - - if (dashIndex != -1) { - int lastDashIndex = fragment.lastIndexOf('-') - - if (dashIndex == lastDashIndex) { - // See jd.gui.api.feature.UriOpenable - throw new InvalidFormatException('fragment: ' + fragment) - } else { - def name = fragment.substring(dashIndex + 1, lastDashIndex) - def descriptor = fragment.substring(lastDashIndex + 1) - - if (descriptor.startsWith('(')) { - for (def method : type.methods) { - if (method.name.equals(name) && method.descriptor.equals(descriptor)) { - sb.append('.').append(method.displayName) - break - } - } - } else { - for (def field : type.fields) { - if (field.name.equals(name) && field.descriptor.equals(descriptor)) { - sb.append('.').append(field.displayName) - break - } - } - } - } - } - - Toolkit.defaultToolkit.systemClipboard.setContents(new StringSelection(sb.toString()), null) - return - } - } - - // Copy path of entry - def path = new File(entry.uri).absolutePath - Toolkit.defaultToolkit.systemClipboard.setContents(new StringSelection(path), null) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/container/EarContainerFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/container/EarContainerFactoryProvider.groovy deleted file mode 100644 index 1f984b34..00000000 --- a/services/src/main/groovy/org/jd/gui/service/container/EarContainerFactoryProvider.groovy +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 java.nio.file.Files -import java.nio.file.InvalidPathException -import java.nio.file.Path - -class EarContainerFactoryProvider implements ContainerFactory { - - String getType() { 'ear' } - - 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.fileSystem.provider().scheme.equals('file') && Files.exists(rootPath.resolve('META-INF/application.xml')) - } catch (InvalidPathException e) { - return false - } - } - } - - Container make(API api, Container.Entry parentEntry, Path rootPath) { - return new EarContainer(api, parentEntry, rootPath) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/container/GenericContainerFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/container/GenericContainerFactoryProvider.groovy deleted file mode 100644 index 09aeb7d4..00000000 --- a/services/src/main/groovy/org/jd/gui/service/container/GenericContainerFactoryProvider.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 - -class GenericContainerFactoryProvider implements ContainerFactory { - - String getType() { 'generic' } - - boolean accept(API api, Path rootPath) { true } - - Container make(API api, Container.Entry parentEntry, Path rootPath) { - return new GenericContainer(api, parentEntry, rootPath) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/container/JarContainerFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/container/JarContainerFactoryProvider.groovy deleted file mode 100644 index 31b4e8eb..00000000 --- a/services/src/main/groovy/org/jd/gui/service/container/JarContainerFactoryProvider.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 java.nio.file.Files -import java.nio.file.InvalidPathException -import java.nio.file.Path - -class JarContainerFactoryProvider implements ContainerFactory { - - String getType() { 'jar' } - - 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.fileSystem.provider().scheme.equals('file') && Files.exists(rootPath.resolve('META-INF')) - } catch (InvalidPathException e) { - return false - } - } - } - - Container make(API api, Container.Entry parentEntry, Path rootPath) { - return new JarContainer(api, parentEntry, rootPath) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/container/WarContainerFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/container/WarContainerFactoryProvider.groovy deleted file mode 100644 index b916cca9..00000000 --- a/services/src/main/groovy/org/jd/gui/service/container/WarContainerFactoryProvider.groovy +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 java.nio.file.Files -import java.nio.file.InvalidPathException -import java.nio.file.Path - -class WarContainerFactoryProvider implements ContainerFactory { - - String getType() { 'war' } - - 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.fileSystem.provider().scheme.equals('file') && Files.exists(rootPath.resolve('WEB-INF')) - } catch (InvalidPathException e) { - return false - } - } - } - - Container make(API api, Container.Entry parentEntry, Path rootPath) { - return new WarContainer(api, parentEntry, rootPath) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.groovy deleted file mode 100644 index d901715c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.groovy +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.FileLoader - -import javax.swing.* -import java.nio.file.Path - -abstract class AbstractFileLoaderProvider implements FileLoader { - protected T load(API api, File file, Path rootPath) { - // Dummy parent container - def parentContainer = new Container() { - String getType() { 'generic' } - Container.Entry getRoot() { null } - } - // Dummy parent entry - def uri = file.toURI() - def path = uri.path - - if (path.endsWith('/')) - path = path.substring(0, path.length()-1) - - def parentEntry = new Container.Entry() { - Collection children = Collections.emptyList() - - Container getContainer() { parentContainer } - Container.Entry getParent() { null } - URI getUri() { uri } - String getPath() { path } - boolean isDirectory() { file.isDirectory() } - long length() { file.length() } - InputStream getInputStream() { file.newInputStream() } - Collection getChildren() { children } - } - def container = api.getContainerFactory(rootPath)?.make(api, parentEntry, rootPath) - - if (container) { - parentEntry.children = container.root.children - - def mainPanel = api.getMainPanelFactory(container)?.make(api, container) - if (mainPanel) { - def data = api.getTreeNodeFactory(parentEntry)?.make(api, parentEntry).userObject - def icon = (data instanceof TreeNodeData) ? data.icon : null - - api.addPanel(file.name, icon, 'Location: ' + file.absolutePath, mainPanel) - return mainPanel - } - } - - return null - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy deleted file mode 100644 index 72829711..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 java.nio.file.Paths - -abstract class AbstractTypeFileLoaderProvider extends AbstractFileLoaderProvider { - - protected boolean load(API api, File file, String pathInFile) { - // Search root path - String pathSuffix = pathInFile - String path = file.path - - while (! path.endsWith(pathSuffix)) { - int index = pathSuffix.indexOf(File.separator) - - if (index == -1) { - pathSuffix = '' - } else { - pathSuffix = pathSuffix.substring(index+1) - } - } - - if (pathSuffix) { - // Init root file - File rootFile = file - int index = pathSuffix.indexOf(File.separator) - - while (index != -1) { - rootFile = rootFile.parentFile - pathSuffix = pathSuffix.substring(index+1) - index = pathSuffix.indexOf(File.separator) - } - rootFile = rootFile.parentFile - - // Create panel - def mainPanel = load(api, rootFile, Paths.get(rootFile.toURI())) - - if (mainPanel instanceof UriOpenable) { - // Open page - pathSuffix = file.absolutePath.substring(rootFile.absolutePath.length()).replace(File.separator, '/') - def rootUri = rootFile.toURI() - def uri = new URI(rootUri.scheme, rootUri.host, rootUri.path + '!' + pathSuffix, null) - mainPanel.openUri(uri) - return true - } else { - return mainPanel != null - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy deleted file mode 100644 index 42ce9487..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/ClassFileLoaderProvider.groovy +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 groovyjarjarasm.asm.ClassReader -import org.jd.gui.api.API - -class ClassFileLoaderProvider extends AbstractTypeFileLoaderProvider { - - String[] getExtensions() { ['class'] } - String getDescription() { 'Class files (*.class)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.class') - } - - boolean load(API api, File file) { - file.withInputStream { is -> - def classReader = new ClassReader(is) - def pathInFile = classReader.className.replace('/', File.separator) + '.class' - - return load(api, file, pathInFile) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/EarFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/EarFileLoaderProvider.groovy deleted file mode 100644 index cf6784c8..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/EarFileLoaderProvider.groovy +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 - -class EarFileLoaderProvider extends ZipFileLoaderProvider { - - String[] getExtensions() { ['ear'] } - String getDescription() { 'Ear files (*.ear)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.ear') - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/JarFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/JarFileLoaderProvider.groovy deleted file mode 100644 index 6c9c0daa..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/JarFileLoaderProvider.groovy +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 - -class JarFileLoaderProvider extends ZipFileLoaderProvider { - - String[] getExtensions() { ['jar'] } - String getDescription() { 'Jar files (*.jar)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.jar') - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy deleted file mode 100644 index 5b3aa6a9..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/JavaFileLoaderProvider.groovy +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.util.regex.Pattern - -class JavaFileLoaderProvider extends AbstractTypeFileLoaderProvider { - - String[] getExtensions() { ['java'] } - String getDescription() { 'Java files (*.java)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.java') - } - - boolean load(API api, File file) { - def pattern = Pattern.compile('(?s)(.*\\s)?package\\s+(\\S+)\\s*;.*') - def matcher = file.text =~ pattern - - if (matcher.matches()) { - // Package name found - def pathInFile = matcher[0][2].replace('.', File.separator) + File.separator + file.name - - return load(api, file, pathInFile) - } else { - // Package name not found - return load(api, file, file.name) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/LogFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/LogFileLoaderProvider.groovy deleted file mode 100644 index fcdf9a63..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/LogFileLoaderProvider.groovy +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.view.component.LogPage - -class LogFileLoaderProvider extends ZipFileLoaderProvider { - - String[] getExtensions() { ['log'] } - String getDescription() { 'Log files (*.log)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.log') - } - - boolean load(API api, File file) { - api.addPanel(file.name, null, 'Location: ' + file.absolutePath, new LogPage(api, file.toURI(), file.text)) - return true - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/WarFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/WarFileLoaderProvider.groovy deleted file mode 100644 index 7156d4ac..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/WarFileLoaderProvider.groovy +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 - -class WarFileLoaderProvider extends ZipFileLoaderProvider { - - String[] getExtensions() { ['war'] } - String getDescription() { 'War files (*.war)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.war') - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/fileloader/ZipFileLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/fileloader/ZipFileLoaderProvider.groovy deleted file mode 100644 index 8b66424c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/fileloader/ZipFileLoaderProvider.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.nio.file.FileSystemNotFoundException -import java.nio.file.FileSystems - -class ZipFileLoaderProvider extends AbstractFileLoaderProvider { - - String[] getExtensions() { ['zip'] } - String getDescription() { 'Zip files (*.zip)' } - - boolean accept(API api, File file) { - return file.exists() && file.canRead() && file.name.toLowerCase().endsWith('.zip') - } - - boolean load(API api, File file) { - def fileUri = file.toURI() - def uri = new URI('jar:' + fileUri.scheme, fileUri.host, fileUri.path + '!/', null) - def fileSystem - - try { - fileSystem = FileSystems.getFileSystem(uri) - } catch (FileSystemNotFoundException e) { - fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()) - } - - if (fileSystem) { - def rootDirectories = fileSystem.rootDirectories.iterator() - - if (rootDirectories.hasNext()) { - return load(api, file, rootDirectories.next()) != null - } - } - - return false - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/DirectoryIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/DirectoryIndexerProvider.groovy deleted file mode 100644 index a6ed570c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/DirectoryIndexerProvider.groovy +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Indexes - -class DirectoryIndexerProvider extends AbstractIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:dir:*'] + externalSelectors } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - int depth = 15 - - try { - depth = Integer.valueOf(api.preferences.get('DirectoryIndexerPreferences.maximumDepth')) - } catch (NumberFormatException ignore) { - } - - index(api, entry, indexes, depth) - } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes, int depth) { - if (depth-- > 0) { - for (def e : entry.children) { - if (e.isDirectory()) { - index(api, e, indexes, depth) - } else { - api.getIndexer(e)?.index(api, e, indexes) - } - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.groovy deleted file mode 100644 index be3bf140..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.groovy +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.xml.AbstractXmlPathFinder - -class EjbJarXmlFileIndexerProvider extends XmlBasedFileIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:META-INF/ejb-jar.xml'] + externalSelectors } - - void index(API api, Container.Entry entry, Indexes indexes) { - super.index(api, entry, indexes) - - new EjbJarXmlPathFinder(entry, indexes).find(entry.inputStream.text) - } - - static class EjbJarXmlPathFinder extends AbstractXmlPathFinder { - Container.Entry entry - Map index - - EjbJarXmlPathFinder(Container.Entry entry, Indexes indexes) { - super([ - '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'); - } - - void handle(String path, String text, int position) { - index.get(text.replace('.', '/')).add(entry); - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.groovy deleted file mode 100644 index 35613c47..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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 - -class MetainfServiceFileIndexerProvider extends AbstractIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*'] + externalSelectors } - - /** - * @return external or local path pattern - */ - Pattern getPathPattern() { externalPathPattern ?: ~/META-INF\/services\/[^\/]+/ } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - def index = indexes.getIndex('typeReferences') - - entry.inputStream.text.eachLine { String line -> - def trim = line.trim() - - if (trim && (trim.charAt(0) != '#')) { - def internalTypeName = trim.replace('.', '/') - - index.get(internalTypeName).add(entry) - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/TextFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/TextFileIndexerProvider.groovy deleted file mode 100644 index 974d9152..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/TextFileIndexerProvider.groovy +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Indexes - -class TextFileIndexerProvider extends AbstractIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { - ['*: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'] + externalSelectors } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - indexes.getIndex('strings').get(entry.inputStream.text).add(entry) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.groovy deleted file mode 100644 index 7f2b8283..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.groovy +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.xml.AbstractXmlPathFinder - -class WebXmlFileIndexerProvider extends XmlBasedFileIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:WEB-INF/web.xml'] + externalSelectors } - - void index(API api, Container.Entry entry, Indexes indexes) { - super.index(api, entry, indexes) - - new WebXmlPathFinder(entry, indexes).find(entry.inputStream.text) - } - - static class WebXmlPathFinder extends AbstractXmlPathFinder { - Container.Entry entry - Map index - - WebXmlPathFinder(Container.Entry entry, Indexes indexes) { - super([ - 'web-app/filter/filter-class', - 'web-app/listener/listener-class', - 'web-app/servlet/servlet-class' - ]) - this.entry = entry - this.index = indexes.getIndex('typeReferences'); - } - - void handle(String path, String text, int position) { - index.get(text.replace('.', '/')).add(entry); - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.groovy deleted file mode 100644 index dbe5dd82..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.groovy +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Indexes - -import javax.xml.stream.XMLInputFactory -import javax.xml.stream.XMLStreamConstants - -class XmlBasedFileIndexerProvider extends AbstractIndexerProvider { - XMLInputFactory factory - - XmlBasedFileIndexerProvider() { - factory = XMLInputFactory.newInstance() - factory.setProperty(XMLInputFactory.SUPPORT_DTD, false) - } - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd'] + externalSelectors } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - def stringSet = new HashSet() - def reader - - try { - reader = factory.createXMLStreamReader(entry.inputStream) - - stringSet.add(reader.version) - stringSet.add(reader.encoding) - stringSet.add(reader.characterEncodingScheme) - - while (reader.hasNext()) { - switch (reader.next()) { - case XMLStreamConstants.START_ELEMENT: - stringSet.add(reader.localName) - for (int i = reader.attributeCount - 1; i >= 0; i--) { - stringSet.add(reader.getAttributeLocalName(i)) - stringSet.add(reader.getAttributeValue(i)) - } - for (int i = reader.namespaceCount - 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.version) - stringSet.add(reader.encoding) - stringSet.add(reader.characterEncodingScheme) - break - case XMLStreamConstants.ENTITY_REFERENCE: - stringSet.add(reader.localName) - stringSet.add(reader.text) - break - case XMLStreamConstants.ATTRIBUTE: - stringSet.add(reader.prefix) - stringSet.add(reader.namespaceURI) - stringSet.add(reader.localName) - stringSet.add(reader.text) - break - case XMLStreamConstants.COMMENT: - case XMLStreamConstants.DTD: - case XMLStreamConstants.CDATA: - case XMLStreamConstants.CHARACTERS: - stringSet.add(reader.text.trim()) - break - case XMLStreamConstants.NAMESPACE: - for (int i = reader.namespaceCount - 1; i >= 0; i--) { - stringSet.add(reader.getNamespacePrefix(i)) - stringSet.add(reader.getNamespaceURI(i)) - } - break - } - } - } catch (Exception ignore) { - } finally { - reader?.close() - } - - def stringIndex = indexes.getIndex('strings') - - for (def string : stringSet) { - if (string) { - stringIndex.get(string).add(entry) - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/XmlFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/XmlFileIndexerProvider.groovy deleted file mode 100644 index 5f9e71ec..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/XmlFileIndexerProvider.groovy +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Indexes - -import javax.xml.stream.XMLInputFactory -import javax.xml.stream.XMLStreamConstants - -class XmlFileIndexerProvider extends AbstractIndexerProvider { - XMLInputFactory factory - - XmlFileIndexerProvider() { - factory = XMLInputFactory.newInstance() - factory.setProperty(XMLInputFactory.SUPPORT_DTD, false) - } - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.xml'] + externalSelectors } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - def stringSet = new HashSet() - def typeReferenceSet = new HashSet() - def reader - - try { - reader = factory.createXMLStreamReader(entry.inputStream) - - stringSet.add(reader.version) - stringSet.add(reader.encoding) - stringSet.add(reader.characterEncodingScheme) - - while (reader.hasNext()) { - switch (reader.next()) { - case XMLStreamConstants.START_ELEMENT: - boolean beanFlag = reader.localName.equals('bean') - - stringSet.add(reader.localName) - for (int i = reader.attributeCount - 1; i >= 0; i--) { - def 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.namespaceCount - 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.version) - stringSet.add(reader.encoding) - stringSet.add(reader.characterEncodingScheme) - break - case XMLStreamConstants.ENTITY_REFERENCE: - stringSet.add(reader.localName) - stringSet.add(reader.text) - break - case XMLStreamConstants.ATTRIBUTE: - stringSet.add(reader.prefix) - stringSet.add(reader.namespaceURI) - stringSet.add(reader.localName) - stringSet.add(reader.text) - break - case XMLStreamConstants.COMMENT: - case XMLStreamConstants.DTD: - case XMLStreamConstants.CDATA: - case XMLStreamConstants.CHARACTERS: - stringSet.add(reader.text.trim()) - break - case XMLStreamConstants.NAMESPACE: - for (int i = reader.namespaceCount - 1; i >= 0; i--) { - stringSet.add(reader.getNamespacePrefix(i)) - stringSet.add(reader.getNamespaceURI(i)) - } - break - } - } - } catch (Exception ignore) { - } finally { - reader?.close() - } - - def stringIndex = indexes.getIndex('strings') - def typeReferenceIndex = indexes.getIndex('typeReferences') - - for (def string : stringSet) { - if (string) { - stringIndex.get(string).add(entry) - } - } - - for (def ref : typeReferenceSet) { - if (ref) { - typeReferenceIndex.get(ref).add(entry) - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/indexer/ZipFileIndexerProvider.groovy b/services/src/main/groovy/org/jd/gui/service/indexer/ZipFileIndexerProvider.groovy deleted file mode 100644 index b66d607f..00000000 --- a/services/src/main/groovy/org/jd/gui/service/indexer/ZipFileIndexerProvider.groovy +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.api.model.Indexes - -class ZipFileIndexerProvider extends AbstractIndexerProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.zip', '*:file:*.jar', '*:file:*.war', '*:file:*.ear'] + externalSelectors } - - @CompileStatic - void index(API api, Container.Entry entry, Indexes indexes) { - for (def e : entry.children) { - if (e.isDirectory()) { - index(api, e, indexes) - } else { - api.getIndexer(e)?.index(api, e, indexes) - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/pastehandler/LogPasteHandler.groovy b/services/src/main/groovy/org/jd/gui/service/pastehandler/LogPasteHandler.groovy deleted file mode 100644 index cac0d0f2..00000000 --- a/services/src/main/groovy/org/jd/gui/service/pastehandler/LogPasteHandler.groovy +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 - -class LogPasteHandler implements PasteHandler { - protected static int counter = 0 - - boolean accept(Object obj) { obj instanceof String } - - void paste(API api, Object obj) { - def title = 'clipboard-' + (++counter) + '.log' - def uri = URI.create('memory://' + title) - def content = obj?.toString() - api.addPanel(title, null, null, new LogPage(api, uri, content)) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.groovy b/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.groovy deleted file mode 100644 index 1cad69c6..00000000 --- a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.groovy +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.JCheckBox -import javax.swing.JPanel -import java.awt.Color -import java.awt.GridLayout - -@CompileStatic -class ClassFileSaverPreferencesProvider extends JPanel implements PreferencesPanel { - - static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileSaverPreferences.escapeUnicodeCharacters' - static final String OMIT_THIS_PREFIX = 'ClassFileSaverPreferences.omitThisPrefix' - static final String REALIGN_LINE_NUMBERS = 'ClassFileSaverPreferences.realignLineNumbers' - static final String WRITE_DEFAULT_CONSTRUCTOR = 'ClassFileSaverPreferences.writeDefaultConstructor' - static final String WRITE_LINE_NUMBERS = 'ClassFileSaverPreferences.writeLineNumbers' - static final String WRITE_METADATA = 'ClassFileSaverPreferences.writeMetadata' - - JCheckBox escapeUnicodeCharactersCheckBox - JCheckBox omitThisPrefixCheckBox - JCheckBox realignLineNumbersCheckBox - JCheckBox writeDefaultConstructorCheckBox - JCheckBox writeLineNumbersCheckBox - JCheckBox writeMetadataCheckBox - - ClassFileSaverPreferencesProvider() { - super(new GridLayout(0,1)) - - escapeUnicodeCharactersCheckBox = new JCheckBox('Escape unicode characters') - omitThisPrefixCheckBox = new JCheckBox("Omit the prefix 'this' if possible") - realignLineNumbersCheckBox = new JCheckBox('Realign line numbers') - writeDefaultConstructorCheckBox = new JCheckBox('Write default constructor') - writeLineNumbersCheckBox = new JCheckBox('Write original line numbers') - writeMetadataCheckBox = new JCheckBox('Write metadata') - - add(escapeUnicodeCharactersCheckBox) - add(omitThisPrefixCheckBox) - add(realignLineNumbersCheckBox) - add(writeDefaultConstructorCheckBox) - add(writeLineNumbersCheckBox) - add(writeMetadataCheckBox) - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'Source Saver' } - String getPreferencesPanelTitle() { 'Class file' } - - public void init(Color errorBackgroundColor) {} - - public boolean isActivated() { true } - - void loadPreferences(Map preferences) { - escapeUnicodeCharactersCheckBox.selected = 'true'.equals(preferences.get(ESCAPE_UNICODE_CHARACTERS)) - omitThisPrefixCheckBox.selected = 'true'.equals(preferences.get(OMIT_THIS_PREFIX)) - realignLineNumbersCheckBox.selected = !'false'.equals(preferences.get(REALIGN_LINE_NUMBERS)) - writeDefaultConstructorCheckBox.selected = 'true'.equals(preferences.get(WRITE_DEFAULT_CONSTRUCTOR)) - writeLineNumbersCheckBox.selected = !'false'.equals(preferences.get(WRITE_LINE_NUMBERS)) - writeMetadataCheckBox.selected = !'false'.equals(preferences.get(WRITE_METADATA)) - } - - void savePreferences(Map preferences) { - preferences.put(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.selected)) - preferences.put(OMIT_THIS_PREFIX, Boolean.toString(omitThisPrefixCheckBox.selected)) - preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.selected)) - preferences.put(WRITE_DEFAULT_CONSTRUCTOR, Boolean.toString(writeDefaultConstructorCheckBox.selected)) - preferences.put(WRITE_LINE_NUMBERS, Boolean.toString(writeLineNumbersCheckBox.selected)) - preferences.put(WRITE_METADATA, Boolean.toString(writeMetadataCheckBox.selected)) - } - - boolean arePreferencesValid() { true } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} -} diff --git a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.groovy b/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.groovy deleted file mode 100644 index 58bd7b16..00000000 --- a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.groovy +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.JCheckBox -import javax.swing.JPanel -import java.awt.Color -import java.awt.GridLayout - -@CompileStatic -class ClassFileViewerPreferencesProvider extends JPanel implements PreferencesPanel { - static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileViewerPreferences.escapeUnicodeCharacters' - static final String OMIT_THIS_PREFIX = 'ClassFileViewerPreferences.omitThisPrefix' - static final String REALIGN_LINE_NUMBERS = 'ClassFileViewerPreferences.realignLineNumbers' - static final String DISPLAY_DEFAULT_CONSTRUCTOR = 'ClassFileViewerPreferences.displayDefaultConstructor' - - PreferencesPanel.PreferencesPanelChangeListener listener = null - JCheckBox escapeUnicodeCharactersCheckBox - JCheckBox omitThisPrefixCheckBox - JCheckBox realignLineNumbersCheckBox - JCheckBox displayDefaultConstructorCheckBox - - ClassFileViewerPreferencesProvider() { - super(new GridLayout(0,1)) - - escapeUnicodeCharactersCheckBox = new JCheckBox('Escape unicode characters') - omitThisPrefixCheckBox = new JCheckBox("Omit the prefix 'this' if possible") - realignLineNumbersCheckBox = new JCheckBox('Realign line numbers') - displayDefaultConstructorCheckBox = new JCheckBox('Display default constructor') - - add(escapeUnicodeCharactersCheckBox) - add(omitThisPrefixCheckBox) - add(realignLineNumbersCheckBox) - add(displayDefaultConstructorCheckBox) - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'Viewer' } - String getPreferencesPanelTitle() { 'Class file' } - - public void init(Color errorBackgroundColor) {} - - public boolean isActivated() { true } - - void loadPreferences(Map preferences) { - escapeUnicodeCharactersCheckBox.selected = !'false'.equals(preferences.get(ESCAPE_UNICODE_CHARACTERS)) - omitThisPrefixCheckBox.selected = 'true'.equals(preferences.get(OMIT_THIS_PREFIX)) - realignLineNumbersCheckBox.selected = 'true'.equals(preferences.get(REALIGN_LINE_NUMBERS)) - displayDefaultConstructorCheckBox.selected = 'true'.equals(preferences.get(DISPLAY_DEFAULT_CONSTRUCTOR)) - } - - void savePreferences(Map preferences) { - preferences.put(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.selected)) - preferences.put(OMIT_THIS_PREFIX, Boolean.toString(omitThisPrefixCheckBox.selected)) - preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.selected)) - preferences.put(DISPLAY_DEFAULT_CONSTRUCTOR, Boolean.toString(displayDefaultConstructorCheckBox.selected)) - } - - boolean arePreferencesValid() { true } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} -} diff --git a/services/src/main/groovy/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.groovy b/services/src/main/groovy/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.groovy deleted file mode 100644 index 2f9cdbd5..00000000 --- a/services/src/main/groovy/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.groovy +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel - -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JTextField -import javax.swing.event.DocumentEvent -import javax.swing.event.DocumentListener -import java.awt.BorderLayout -import java.awt.Color - -@CompileStatic -class DirectoryIndexerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener { - static final int MAX_VALUE = 30 - static final String MAXIMUM_DEPTH_KEY = 'DirectoryIndexerPreferences.maximumDepth' - - PreferencesPanel.PreferencesPanelChangeListener listener = null - JTextField maximumDepthTextField - Color errorBackgroundColor = Color.RED - Color defaultBackgroundColor - - DirectoryIndexerPreferencesProvider() { - super(new BorderLayout()) - - add(new JLabel('Maximum depth (1..' + MAX_VALUE + '): '), BorderLayout.WEST) - - maximumDepthTextField = new JTextField() - maximumDepthTextField.document.addDocumentListener(this) - add(maximumDepthTextField, BorderLayout.CENTER) - - defaultBackgroundColor = maximumDepthTextField.background - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'Indexer' } - String getPreferencesPanelTitle() { 'Directory exploration' } - - public void init(Color errorBackgroundColor) { - this.errorBackgroundColor = errorBackgroundColor - } - - public boolean isActivated() { true } - - void loadPreferences(Map preferences) { - maximumDepthTextField.text = preferences.get(MAXIMUM_DEPTH_KEY) ?: '15' - maximumDepthTextField.setCaretPosition(maximumDepthTextField.text.size()) - } - - void savePreferences(Map preferences) { - preferences.put(MAXIMUM_DEPTH_KEY, maximumDepthTextField.text) - } - - boolean arePreferencesValid() { - try { - int i = Integer.valueOf(maximumDepthTextField.text) - return (i > 0) && (i <= MAX_VALUE) - } catch (NumberFormatException ignore) { - return false - } - } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) { - this.listener = listener - } - - // --- DocumentListener --- // - void insertUpdate(DocumentEvent e) { onTextChange() } - void removeUpdate(DocumentEvent e) { onTextChange() } - void changedUpdate(DocumentEvent e) { onTextChange() } - - void onTextChange() { - maximumDepthTextField.background = arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor - listener?.preferencesPanelChanged(this) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.groovy b/services/src/main/groovy/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.groovy deleted file mode 100644 index ff0f273c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.groovy +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.spi.PreferencesPanel -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea -import org.fife.ui.rsyntaxtextarea.Theme - -import javax.swing.* -import javax.swing.event.DocumentEvent -import javax.swing.event.DocumentListener -import java.awt.* - -@CompileStatic -class ViewerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener { - static final int MIN_VALUE = 2 - static final int MAX_VALUE = 40 - static final String FONT_SIZE_KEY = 'ViewerPreferences.fontSize' - - PreferencesPanel.PreferencesPanelChangeListener listener = null - JTextField fontSizeTextField - Color errorBackgroundColor = Color.RED - Color defaultBackgroundColor - - ViewerPreferencesProvider() { - super(new BorderLayout()) - - add(new JLabel('Font size (' + MIN_VALUE + '..' + MAX_VALUE + '): '), BorderLayout.WEST) - - fontSizeTextField = new JTextField() - fontSizeTextField.document.addDocumentListener(this) - add(fontSizeTextField, BorderLayout.CENTER) - - defaultBackgroundColor = fontSizeTextField.background - } - - // --- PreferencesPanel --- // - String getPreferencesGroupTitle() { 'Viewer' } - String getPreferencesPanelTitle() { 'Appearance' } - - public void init(Color errorBackgroundColor) { - this.errorBackgroundColor = errorBackgroundColor - } - - public boolean isActivated() { true } - - void loadPreferences(Map preferences) { - def fontSize = preferences.get(FONT_SIZE_KEY) - - if (! fontSize) { - // Search default value for the current platform - def textArea = new RSyntaxTextArea() - - def theme = Theme.load(getClass().classLoader.getResourceAsStream('rsyntaxtextarea/themes/eclipse.xml')) - theme.apply(textArea) - - fontSize = textArea.font.size - } - - fontSizeTextField.text = fontSize - fontSizeTextField.setCaretPosition(fontSizeTextField.text.size()) - } - - void savePreferences(Map preferences) { - preferences.put(FONT_SIZE_KEY, fontSizeTextField.text) - } - - boolean arePreferencesValid() { - try { - int i = Integer.valueOf(fontSizeTextField.text) - return (i >= MIN_VALUE) && (i <= MAX_VALUE) - } catch (NumberFormatException ignore) { - return false - } - } - - void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) { - this.listener = listener - } - - // --- DocumentListener --- // - void insertUpdate(DocumentEvent e) { onTextChange() } - void removeUpdate(DocumentEvent e) { onTextChange() } - void changedUpdate(DocumentEvent e) { onTextChange() } - - void onTextChange() { - fontSizeTextField.background = arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor - listener?.preferencesPanelChanged(this) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.groovy b/services/src/main/groovy/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.groovy deleted file mode 100644 index c1ef083d..00000000 --- a/services/src/main/groovy/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.groovy +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.spi.SourceSaver - -import java.nio.file.Files -import java.nio.file.Path - -class DirectorySourceSaverProvider extends AbstractSourceSaverProvider { - - /** - * @return local + optional external selectors - */ - @Override String[] getSelectors() { ['*:dir:*'] + externalSelectors } - - @Override String getSourcePath(Container.Entry entry) { entry.path + '.src.zip' } - - @Override int getFileCount(API api, Container.Entry entry) { getFileCount(api, entry.children) } - - @CompileStatic - protected int getFileCount(API api, Collection entries) { - int count = 0 - - for (def e : entries) { - count += api.getSourceSaver(e)?.getFileCount(api, e) - } - - return count - } - - @Override - @CompileStatic - public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) { - Path path = rootPath.resolve(entry.getPath()) - - Files.createDirectories(path) - - saveContent(api, controller, listener, rootPath, path, entry); - } - - @Override - @CompileStatic - public void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) { - for (def e : getChildren(entry)) { - if (controller.isCancelled()) { - break - } - - api.getSourceSaver(e)?.save(api, controller, listener, rootPath, e) - } - } - - protected Collection getChildren(Container.Entry entry) { entry.children } -} diff --git a/services/src/main/groovy/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.groovy b/services/src/main/groovy/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.groovy deleted file mode 100644 index 375c306c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.spi.SourceSaver - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardCopyOption - -class FileSourceSaverProvider extends AbstractSourceSaverProvider { - - /** - * @return local + optional external selectors - */ - @Override String[] getSelectors() { ['*:file:*'] + externalSelectors } - - @Override String getSourcePath(Container.Entry entry) { entry.path } - - @Override int getFileCount(API api, Container.Entry entry) { 1 } - - @Override - @CompileStatic - 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 - @CompileStatic - void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) { - listener.pathSaved(path) - - entry.inputStream.withStream { InputStream is -> - Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.groovy b/services/src/main/groovy/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.groovy deleted file mode 100644 index 700d7c69..00000000 --- a/services/src/main/groovy/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.JarContainerEntryUtil - -class PackageSourceSaverProvider extends DirectorySourceSaverProvider { - /** - * @return local + optional external selectors - */ - @Override String[] getSelectors() { ['jar:dir:*', 'war:dir:*', 'ear:dir:*'] + externalSelectors } - - @Override - protected Collection getChildren(Container.Entry entry) { - JarContainerEntryUtil.removeInnerTypeEntries(entry.children) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.groovy b/services/src/main/groovy/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.groovy deleted file mode 100644 index 465ca91b..00000000 --- a/services/src/main/groovy/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.groovy +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.spi.SourceSaver - -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - -class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider { - - /** - * @return local + optional external selectors - */ - @Override String[] getSelectors() { ['*:file:*.zip', '*:file:*.jar', '*:file:*.war', '*:file:*.ear'] + externalSelectors } - - @Override - @CompileStatic - public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) { - def sourcePath = getSourcePath(entry) - def path = rootPath.resolve(sourcePath) - def parentPath = path.parent - - if (parentPath && !Files.exists(parentPath)) { - Files.createDirectories(parentPath) - } - - def tmpFile = File.createTempFile('jd-gui.', '.tmp.zip') - tmpFile.delete() - - def tmpFileUri = tmpFile.toURI() - def tmpArchiveUri = new URI('jar:' + tmpFileUri.scheme, tmpFileUri.host, tmpFileUri.path + '!/', null) - def tmpArchiveFs = FileSystems.newFileSystem(tmpArchiveUri, [create: 'true']); - def tmpArchiveRootPath = tmpArchiveFs.getPath('/') - - saveContent(api, controller, listener, tmpArchiveRootPath, tmpArchiveRootPath, entry) - - tmpArchiveFs.close() - - def tmpPath = Paths.get(tmpFile.absolutePath) - - Files.move(tmpPath, path) - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index ff82b15b..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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.view.data.TreeNodeBean - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { - - static class BaseTreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, PageCreator { - Container.Entry entry - PageAndTipFactory factory; - URI uri - - BaseTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { - super(userObject) - this.entry = entry - this.factory = factory - - if (fragment) { - def uri = entry.uri - this.uri = new URI(uri.scheme, uri.host, uri.path, fragment) - } else { - this.uri = entry.uri - } - } - - // --- ContainerEntryGettable --- // - Container.Entry getEntry() { entry } - - // --- UriGettable --- // - URI getUri() { uri } - - // --- PageCreator --- // - public T createPage(API api) { - // Lazy 'tip' initialization - userObject.tip = factory.makeTip(api, entry) - return factory.makePage(api, entry) - } - } - - static class FileTreeNode extends BaseTreeNode implements TreeNodeExpandable { - boolean initialized - - FileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) { - this(entry, null, userObject, pageAndTipFactory) - } - - FileTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { - super(entry, fragment, userObject, factory) - initialized = false - // Add dummy node - add(new DefaultMutableTreeNode()) - } - - // --- TreeNodeExpandable --- // - void populateTreeNode(API api) { - if (!initialized) { - removeAllChildren() - // Create type node - def types = api.getTypeFactory(entry)?.make(api, entry) - - for (def type : types) { - add(new TypeTreeNode(entry, type, new TreeNodeBean(label: type.displayTypeName, icon: type.icon), factory)) - } - - initialized = true - } - } - } - - static class TypeTreeNode extends BaseTreeNode implements TreeNodeExpandable { - boolean initialized - Type type - - TypeTreeNode(Container.Entry entry, Type type, Object userObject, PageAndTipFactory factory) { - super(entry, type.name, userObject, factory) - this.initialized = false - this.type = type - // Add dummy node - add(new DefaultMutableTreeNode()) - } - - // --- TreeNodeExpandable --- // - void populateTreeNode(API api) { - if (!initialized) { - removeAllChildren() - - def typeName = type.name - - // Create inner types - type.innerTypes.sort { t1, t2 -> - t1.name.compareTo(t2.name) - }.each { - add(new TypeTreeNode(entry, it, new TreeNodeBean(label: it.displayInnerTypeName, icon: it.icon), factory)) - } - - // Create fields - type.fields.collect { - def fragment = typeName + '-' + it.name + '-' + it.descriptor - return new FieldOrMethodBean(fragment:fragment, label:it.displayName, icon:it.icon) - }.sort { f1, f2 -> - f1.label.compareTo(f2.label) - }.each { - add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), factory)) - } - - // Create methods - type.methods.grep { - !it.name.equals('') - }.collect { - def fragment = typeName + '-' + it.name + '-' + it.descriptor - return new FieldOrMethodBean(fragment:fragment, label:it.displayName, icon:it.icon) - }.sort { m1, m2 -> - m1.label.compareTo(m2.label) - }.each { - add(new FieldOrMethodTreeNode(entry, it.fragment, new TreeNodeBean(label: it.label, icon: it.icon), factory)) - } - - initialized = true - } - } - } - - static class FieldOrMethodTreeNode extends BaseTreeNode { - FieldOrMethodTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) { - super(entry, fragment, userObject, factory) - } - } - - static class FieldOrMethodBean { - String fragment, label - Icon icon - } - - interface PageAndTipFactory { - public T makePage(API api, Container.Entry entry); - public String makeTip(API api, Container.Entry entry); - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index ed50dacc..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.ClassFilePage -import org.jd.gui.view.data.TreeNodeBean - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { - static final ImageIcon CLASS_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/classf_obj.png')) - - static final Factory FACTORY = new Factory(); - - static { - // Early class loading - try { - new ClassFilePage(null, null) - } catch (Exception ignore) {} - } - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.class'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - - return new FileTreeNode( - entry, - new TreeNodeBean(label:name, icon:CLASS_FILE_ICON), - FACTORY - ) - } - - static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { - public T makePage(API a, Container.Entry e) { - return new ClassFilePage(a, e) - } - - public String makeTip(API api, Container.Entry entry) { - def file = new File(entry.container.root.uri) - def tip = "Location: $file.path" - - entry.inputStream.withStream { is -> - is.skip(4) // Skip magic number - int minorVersion = readUnsignedShort(is) - int majorVersion = readUnsignedShort(is) - - if (majorVersion >= 49) { - tip += "
Java compiler version: ${majorVersion - (49-5)} ($majorVersion.$minorVersion)" - } else if (majorVersion >= 45) { - tip += "
Java compiler version: 1.${majorVersion - (45-1)} ($majorVersion.$minorVersion)" - } - } - - tip += "" - - return tip - } - - /** - * @see java.io.DataInputStream#readUnsignedShort() - */ - @CompileStatic - 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) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 562f3a4e..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.ImageIcon -import javax.swing.JComponent -import javax.swing.tree.DefaultMutableTreeNode - -class CssFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/css_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.css'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_CSS - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.groovy deleted file mode 100644 index fa60511f..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 - -class DirectoryTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/folder.gif')) - static final ImageIcon OPEN_ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/folder_open.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:dir:*'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - Collection entries = entry.children - - // Aggregate directory names - while (entries.size() == 1) { - Entry child = entries[0] - if (!child.isDirectory() || api.getTreeNodeFactory(child) != this || entry.container != child.container) break - entry = child - entries = entry.children - } - - def label = entry.path.substring(lastSlashIndex+1) - def node = new TreeNode(entry, new TreeNodeBean(label:label, icon:getIcon(), openIcon:getOpenIcon())) - - if (entries.size() > 0) { - // Add dummy node - node.add(new DefaultMutableTreeNode()) - } - - return node - } - - ImageIcon getIcon() { ICON } - ImageIcon getOpenIcon() { OPEN_ICON } - - static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, TreeNodeExpandable { - Container.Entry entry - boolean initialized - - TreeNode(Container.Entry entry, Object userObject) { - super(userObject) - this.entry = entry - this.initialized = false - } - - // --- ContainerEntryGettable --- // - Container.Entry getEntry() { entry } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- TreeNodeExpandable --- // - void populateTreeNode(API api) { - if (!initialized) { - removeAllChildren() - - Collection entries = getChildren() - - while (entries.size() == 1) { - Entry child = entries[0] - if (!child.isDirectory() || api.getTreeNodeFactory(child) != this) { - break - } - entries = child.children - } - - for (Entry entry : entries) { - TreeNodeFactory factory = api.getTreeNodeFactory(entry) - if (factory) { - add(factory.make(api, entry)) - } - } - - initialized = true - } - } - - Collection getChildren() { entry.children } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 88e3305d..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class DtdFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(DtdFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/dtd_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.dtd'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_DTD - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 657ddd74..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 - -class EarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ear_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.ear'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - def node = new TreeNode(entry, 'ear', new TreeNodeBean(label:name, icon:ICON)) - // Add dummy node - node.add(new DefaultMutableTreeNode()) - return node - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index b4d0a0c8..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 - -class EjbJarXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/xml_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['jar:file:META-INF/ejb-jar.xml'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - return new TreeNode(entry, new TreeNodeBean(label:'ejb-jar.xml', icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - // --- PageCreator --- // - public T createPage(API api) { - return new EjbJarXmlFilePage(api, entry) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.groovy deleted file mode 100644 index c4deb8fb..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - -class FileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(FileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/file_plain_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON)) - } - - static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable { - Container.Entry entry - - TreeNode(Container.Entry entry, Object userObject) { - super(userObject) - this.entry = entry - } - - // --- ContainerEntryGettable --- // - Container.Entry getEntry() { entry } - - // --- UriGettable --- // - URI getUri() { entry.uri } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 6dbb2fd3..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class HtmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/html_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.html', '*:file:*.xhtml'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_HTML - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 97b91f0f..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.JarContainerEntryUtil -import org.jd.gui.view.data.TreeNodeBean - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class JarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { - static final ImageIcon JAR_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/jar_obj.png')) - static final ImageIcon EJB_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ejbmodule_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.jar'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - def icon = isAEjbModule(entry) ? EJB_FILE_ICON : JAR_FILE_ICON - def node = new TreeNode(entry, 'jar', new TreeNodeBean(label:name, icon:icon)) - // Add dummy node - node.add(new DefaultMutableTreeNode()) - return node - } - - static boolean isAEjbModule(Container.Entry entry) { - return entry.children?.find { it.path.equals('META-INF') }?.children?.find { it.path.equals('META-INF/ejb-jar.xml') } - } - - static class TreeNode extends ZipFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, String containerType, Object userObject) { - super(entry, containerType, userObject) - } - - Collection getChildren() { JarContainerEntryUtil.removeInnerTypeEntries(entry.children) } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 693d171b..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 - -class JavaFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider { - static final ImageIcon JAVA_FILE_ICON = new ImageIcon(JavaFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/jcu_obj.png')) - - static final Factory FACTORY = new Factory(); - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.java'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - - return new FileTreeNode( - entry, - new TreeNodeBean(label:name, icon:JAVA_FILE_ICON), - FACTORY - ) - } - - static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory { - public T makePage(API a, Container.Entry e) { - return new JavaFilePage(a, e) - } - - public String makeTip(API api, Container.Entry entry) { - def file = new File(entry.container.root.uri) - return "Location: $file.path" - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 0b0ad933..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class JavascriptFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(JavascriptFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/js_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.js'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT - } - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 583fa66a..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class JsonFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(JsonFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ascii_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.json'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_JSON - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 98b1f5bb..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.ImageIcon -import javax.swing.JComponent -import javax.swing.tree.DefaultMutableTreeNode - -class JspFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/html_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.jsp', '*:file:*.jspf'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_JSP - } - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 6252b79a..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 - -class ManifestFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/manifest_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:META-INF/MANIFEST.MF'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - return new TreeNode(entry, new TreeNodeBean(label:'MANIFEST.MF', icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - // --- PageCreator --- // - public T createPage(API api) { - return new ManifestFilePage(api, entry) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.groovy deleted file mode 100644 index ecda1edd..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.ImageIcon -import java.util.regex.Pattern - -class MetainfDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(MetainfDirectoryTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/inf_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['jar:dir:*', 'war:dir:*', 'ear:dir:*'] + externalSelectors } - - /** - * @return external or local path pattern - */ - Pattern getPathPattern() { externalPathPattern ?: ~/(WEB-INF|(WEB-INF\/classes\/)?META-IN(F|F\/.*))/ } - - ImageIcon getIcon() { ICON } - ImageIcon getOpenIcon() { null } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 2faf2e5c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.OneTypeReferenceByLinePage -import org.jd.gui.view.data.TreeNodeBean - -import javax.swing.ImageIcon -import javax.swing.JComponent -import javax.swing.tree.DefaultMutableTreeNode -import java.util.regex.Pattern - -class MetainfServiceFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ascii_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*'] + externalSelectors } - - /** - * @return external or local path pattern - */ - Pattern getPathPattern() { externalPathPattern ?: ~/META-INF\/services\/[^\/]+/ } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - // --- PageCreator --- // - public T createPage(API api) { - return new OneTypeReferenceByLinePage(api, entry) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.groovy deleted file mode 100644 index 87c9b7f2..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.JarContainerEntryUtil -import org.jd.gui.view.data.TreeNodeBean - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class PackageTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(PackageTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/package_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['jar:dir:*'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - Collection entries = entry.children - - // Aggregate directory names - while (entries.size() == 1) { - Container.Entry child = entries[0] - if (!child.isDirectory() || api.getTreeNodeFactory(child) != this || entry.container != child.container) break - entry = child - entries = entry.children - } - - def label = entry.path.substring(lastSlashIndex+1).replace('/', '.') - def node = new TreeNode(entry, new TreeNodeBean(label:label, icon:getIcon(), openIcon:getOpenIcon())) - - if (entries.size() > 0) { - // Add dummy node - node.add(new DefaultMutableTreeNode()) - } - - return node - } - - ImageIcon getIcon() { ICON } - ImageIcon getOpenIcon() { null } - - static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - Collection getChildren() { - return JarContainerEntryUtil.removeInnerTypeEntries(entry.children) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index db38a975..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class PropertiesFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(PropertiesFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ascii_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.properties'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 1bc9bfbe..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class SqlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(SqlFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/sql_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.sql'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_SQL - } - } - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 0ddb89a5..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.TextPage -import org.jd.gui.view.data.TreeNodeBean -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea -import org.fife.ui.rsyntaxtextarea.Theme -import org.fife.ui.rtextarea.Gutter - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class TextFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/ascii_obj.png')) - - static { - // Early class loading - new Gutter(new RSyntaxTextArea()) - Theme.load(TextFileTreeNodeFactoryProvider.class.classLoader.getResourceAsStream('rsyntaxtextarea/themes/eclipse.xml')) - } - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.txt', '*:file:*.md', '*:file:*.SF', '*:file:*.policy', '*:file:*.yaml', '*:file:*.yml'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new Page(entry) - } - } - - static class Page extends TextPage implements UriGettable { - Container.Entry entry - - Page(Container.Entry entry) { - this.entry = entry - setText(entry.inputStream.text) - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - } -} \ No newline at end of file diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 569a3f6c..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.ImageIcon -import javax.swing.tree.DefaultMutableTreeNode - -class WarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/war_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.war'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - def node = new TreeNode(entry, 'war', new TreeNodeBean(label:name, icon:ICON)) - // Add dummy node - node.add(new DefaultMutableTreeNode()) - return node - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.groovy deleted file mode 100644 index df0d9df7..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 - -class WarPackageTreeNodeFactoryProvider extends PackageTreeNodeFactoryProvider { - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['war:dir:*'] + externalSelectors } - - /** - * @return external or local path pattern - */ - Pattern getPathPattern() { externalPathPattern ?: ~/WEB-INF\/classes\/.*/ } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 63a2df7e..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.ImageIcon -import javax.swing.JComponent -import javax.swing.tree.DefaultMutableTreeNode - - -class WebXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/xml_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['war:file:WEB-INF/web.xml'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - return new TreeNode(entry, new TreeNodeBean(label:'web.xml', icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - // --- PageCreator --- // - public T createPage(API api) { - return new WebXmlFilePage(api, entry) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.groovy deleted file mode 100644 index 5e0e73f3..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.* - -class WebinfClassesDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(WebinfClassesDirectoryTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/packagefolder_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['war:dir:WEB-INF/classes'] + externalSelectors } - - ImageIcon getIcon() { ICON } - ImageIcon getOpenIcon() { null } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.groovy deleted file mode 100644 index 7d9b381e..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.* - -class WebinfLibDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(WebinfLibDirectoryTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/archivefolder_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['war:dir:WEB-INF/lib'] + externalSelectors } - - ImageIcon getIcon() { ICON } - ImageIcon getOpenIcon() { null } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index ec7b1c87..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.* -import javax.swing.tree.DefaultMutableTreeNode - -class XmlBasedFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(XmlBasedFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/xml_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.xsl', '*:file:*.xslt', '*:file:*.xsd', '*:file:*.tld', '*:file:*.wsdl'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new TextFileTreeNodeFactoryProvider.Page(entry) { - String getSyntaxStyle() { - SyntaxConstants.SYNTAX_STYLE_XML - } - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 401edfd6..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - -class XmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(XmlFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/xml_obj.gif')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.xml'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - return new TreeNode(entry, new TreeNodeBean(label:name, icon:ICON, tip:"Location: $entry.uri.path")) - } - - static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode { - TreeNode(Container.Entry entry, Object userObject) { - super(entry, userObject) - } - - public T createPage(API api) { - return new XmlFilePage(api, entry) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.groovy b/services/src/main/groovy/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.groovy deleted file mode 100644 index 39e0673d..00000000 --- a/services/src/main/groovy/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.groovy +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 - -class ZipFileTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { - static final ImageIcon ICON = new ImageIcon(ZipFileTreeNodeFactoryProvider.class.classLoader.getResource('org/jd/gui/images/zip_obj.png')) - - /** - * @return local + optional external selectors - */ - String[] getSelectors() { ['*:file:*.zip'] + externalSelectors } - - public T make(API api, Container.Entry entry) { - int lastSlashIndex = entry.path.lastIndexOf('/') - def name = entry.path.substring(lastSlashIndex+1) - def node = new TreeNode(entry, 'generic', new TreeNodeBean(label:name, icon:ICON)) - // Add dummy node - node.add(new DefaultMutableTreeNode()) - return node - } - - static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode { - String ct - - TreeNode(Container.Entry entry, String containerType, Object userObject) { - super(entry, userObject) - ct = containerType - } - - // --- TreeNodeExpandable --- // - void populateTreeNode(API api) { - if (!initialized) { - removeAllChildren() - - for (def e : getChildren()) { - TreeNodeFactory factory = api.getTreeNodeFactory(e) - if (factory) { - add(factory.make(api, e)) - } - } - - initialized = true - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/service/uriloader/FileUriLoaderProvider.groovy b/services/src/main/groovy/org/jd/gui/service/uriloader/FileUriLoaderProvider.groovy deleted file mode 100644 index 4e2b4a03..00000000 --- a/services/src/main/groovy/org/jd/gui/service/uriloader/FileUriLoaderProvider.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.UriLoader - -class FileUriLoaderProvider implements UriLoader { - - String[] getSchemes() { ['file'] } - - boolean accept(API api, URI uri) { 'file'.equals(uri.scheme) } - - boolean load(API api, URI uri) { - File file = new File(uri.path) - return api.getFileLoader(file)?.load(api, file) - } -} diff --git a/services/src/main/groovy/org/jd/gui/util/JarContainerEntryUtil.groovy b/services/src/main/groovy/org/jd/gui/util/JarContainerEntryUtil.groovy deleted file mode 100644 index 4642eaaa..00000000 --- a/services/src/main/groovy/org/jd/gui/util/JarContainerEntryUtil.groovy +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 - -import groovyjarjarasm.asm.ClassReader -import groovyjarjarasm.asm.tree.ClassNode -import groovyjarjarasm.asm.tree.InnerClassNode -import org.jd.gui.api.model.Container - -class JarContainerEntryUtil { - static Collection removeInnerTypeEntries(Collection entries) { - def potentialOuterTypePaths = new HashSet() - def filtredSubEntries - - for (def e : entries) { - if (!e.isDirectory()) { - String p = e.path - - 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 { - def innerTypePaths = new HashSet() - - for (def e : entries) { - if (!e.isDirectory() && potentialOuterTypePaths.contains(e.path)) { - populateInnerTypePaths(innerTypePaths, e) - } - } - - filtredSubEntries = new ArrayList() - - for (def e : entries) { - if (!e.isDirectory()) { - String p = e.path - - 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) - } - } - - return filtredSubEntries.sort() - } - - protected static void populateInnerTypePaths(HashSet innerTypePaths, Container.Entry entry) { - def classNode = new ClassNode() - def classReader = entry.inputStream.withStream { InputStream is -> new ClassReader(is) } - def p = entry.path - def prefixPath = p.substring(0, p.length() - classReader.className.length() - 6) - - classReader.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) - - for (def obj : classNode.innerClasses) { - def innerClass = obj as InnerClassNode - innerTypePaths.add(prefixPath + innerClass.name + '.class') - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/AbstractTextPage.groovy b/services/src/main/groovy/org/jd/gui/view/component/AbstractTextPage.groovy deleted file mode 100644 index c7ec31d4..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/AbstractTextPage.groovy +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.fife.ui.rsyntaxtextarea.DocumentRange -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea -import org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit -import org.fife.ui.rsyntaxtextarea.SyntaxConstants -import org.fife.ui.rsyntaxtextarea.Theme -import org.fife.ui.rsyntaxtextarea.folding.FoldManager -import org.fife.ui.rtextarea.RTextScrollPane -import org.fife.ui.rtextarea.SearchContext -import org.fife.ui.rtextarea.SearchEngine - -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.MouseWheelEvent -import java.awt.event.MouseWheelListener - -/* - * Initially "AbstractTextPage" and "TextPage" were merged, but after a 7th interface has been added, Gradle 2.4/Groovyc 2.3 - * throws "java.lang.OutOfMemoryError: Java heap space". Only valid workaround : split "TextPage" in two files. - */ -@CompileStatic -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.classLoader.getResource('org/jd/gui/images/plus.png')) - protected static final ImageIcon EXPANDED_ICON = new ImageIcon(AbstractTextPage.class.classLoader.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 - - AbstractTextPage() { - super(new BorderLayout()) - - textArea = newRSyntaxTextArea() - textArea.setSyntaxEditingStyle(getSyntaxStyle()) - textArea.setCodeFoldingEnabled(true); - textArea.setAntiAliasingEnabled(true); - textArea.caretPosition = 0 - textArea.editable = false - textArea.dropTarget = null - textArea.popupMenu = null - textArea.addMouseListener(new MouseAdapter() { - void mouseClicked(MouseEvent e) { - if (e.clickCount == 2) { - textArea.markAllHighlightColor = DOUBLE_CLICK_HIGHLIGHT_COLOR - SearchEngine.markAll(textArea, newSearchContext(textArea.selectedText, true, true, true, false)) - } - } - }) - - def ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.defaultToolkit.menuShortcutKeyMask) - def ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.defaultToolkit.menuShortcutKeyMask) - def inputMap = textArea.inputMap - inputMap.put(ctrlA, 'none') - inputMap.put(ctrlC, 'none') - inputMap.put(ctrlV, 'none') - - def theme = Theme.load(getClass().classLoader.getResourceAsStream('rsyntaxtextarea/themes/eclipse.xml')) - theme.apply(textArea) - - scrollPane = new RTextScrollPane(textArea) - scrollPane.foldIndicatorEnabled = true - scrollPane.font = textArea.font - - def mouseWheelListeners = scrollPane.getMouseWheelListeners() - - // Remove default listeners - for (def listener : mouseWheelListeners) { - scrollPane.removeMouseWheelListener(listener) - } - - scrollPane.addMouseWheelListener(new MouseWheelListener() { - void mouseWheelMoved(MouseWheelEvent e) { - if ((e.modifiers & (Event.META_MASK|Event.CTRL_MASK)) != 0) { - int x = e.x + scrollPane.x - textArea.x - int y = e.y + scrollPane.y - textArea.y - int offset = textArea.viewToModel(new Point(x, y)) - - // Update font size - if (e.wheelRotation > 0) { - INCREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea) - } else { - DECREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea) - } - - // Save preferences - if (preferences) { - preferences.put(FONT_SIZE_KEY, String.valueOf(textArea.font.size)) - } - - Rectangle newRectangle = textArea.modelToView(offset) - int newY = newRectangle.@y + (newRectangle.@height >> 1) - - // Scroll - Point viewPosition = scrollPane.viewport.viewPosition - viewPosition.@y = Math.max(viewPosition.@y + newY - y, 0) - scrollPane.viewport.viewPosition = viewPosition - } else { - // Call default listeners - for (def listener : mouseWheelListeners) { - listener.mouseWheelMoved(e) - } - } - } - }) - - def gutter = scrollPane.gutter - gutter.setFoldIcons(COLLAPSED_ICON, EXPANDED_ICON) - gutter.foldIndicatorForeground = gutter.borderColor - - add(scrollPane, BorderLayout.CENTER) - add(new ErrorStrip(textArea), BorderLayout.LINE_END) - } - - protected RSyntaxTextArea newRSyntaxTextArea() { new RSyntaxTextArea() } - - String getText() { textArea.text } - - void setText(String text) { - textArea.text = text - textArea.caretPosition = 0 - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_NONE } - - /** - * @see org.fife.ui.rsyntaxtextarea.RSyntaxUtilities#selectAndPossiblyCenter - * Force center and do not select - */ - void setCaretPositionAndCenter(DocumentRange range) { - int start = range.startOffset - int end = range.endOffset - boolean foldsExpanded = false - FoldManager fm = textArea.foldManager - - if (fm.isCodeFoldingSupportedAndEnabled()) { - foldsExpanded = fm.ensureOffsetNotInClosedFold(start) - foldsExpanded |= fm.ensureOffsetNotInClosedFold(end) - } - - if (!foldsExpanded) { - try { - Rectangle r = textArea.modelToView(start) - - if (r) { - // Visible - setCaretPositionAndCenter(start, end, r) - } else { - // Not visible yet - SwingUtilities.invokeLater(new Runnable() { - void run() { - r = textArea.modelToView(start) - if (r) { - setCaretPositionAndCenter(start, end, r) - } - } - }) - } - } catch (BadLocationException ignore) { - } - } - } - - protected void setCaretPositionAndCenter(int start, int end, Rectangle r) { - if (end != start) { - r = r.union(textArea.modelToView(end)) - } - - Rectangle visible = textArea.visibleRect - - // visible.@x = r.@x - (visible.@width - r.@width) / 2 as int - visible.@y = r.@y - (visible.@height - r.@height) / 2 as int - - Rectangle bounds = textArea.bounds - Insets i = textArea.insets - //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.caretPosition = start - } - - // --- LineNumberNavigable --- // - int getMaximumLineNumber() { - return textArea.getLineOfOffset(textArea.document.length) + 1 - } - - void goToLineNumber(int lineNumber) { - textArea.caretPosition = textArea.getLineStartOffset(lineNumber-1) - } - - boolean checkLineNumber(int lineNumber) { true } - - // --- ContentSearchable --- // - boolean highlightText(String text, boolean caseSensitive) { - if (text.length() > 1) { - textArea.markAllHighlightColor = SEARCH_HIGHLIGHT_COLOR - textArea.caretPosition = textArea.selectionStart - - def context = newSearchContext(text, caseSensitive, false, true, false) - def result = SearchEngine.find(textArea, context) - - if (!result.wasFound()) { - textArea.caretPosition = 0 - result = SearchEngine.find(textArea, context) - } - - return result.wasFound() - } else { - return true - } - } - - void findNext(String text, boolean caseSensitive) { - if (text.length() > 1) { - textArea.markAllHighlightColor = SEARCH_HIGHLIGHT_COLOR - - def context = newSearchContext(text, caseSensitive, false, true, false) - def result = SearchEngine.find(textArea, context) - - if (!result.wasFound()) { - textArea.caretPosition = 0 - SearchEngine.find(textArea, context) - } - } - } - - void findPrevious(String text, boolean caseSensitive) { - if (text.length() > 1) { - textArea.markAllHighlightColor = SEARCH_HIGHLIGHT_COLOR - - def context = newSearchContext(text, caseSensitive, false, false, false) - def result = SearchEngine.find(textArea, context) - - if (!result.wasFound()) { - textArea.caretPosition = textArea.document.length - SearchEngine.find(textArea, context) - } - } - } - - protected SearchContext newSearchContext( - String searchFor, boolean matchCase, boolean wholeWord, boolean searchForward, boolean regexp) { - def context = new SearchContext(searchFor, matchCase) - context.markAll = true - context.wholeWord = wholeWord - context.searchForward = searchForward - context.regularExpression = regexp - return context - } - - // --- UriOpenable --- // - boolean openUri(URI uri) { - def query = uri.query - - if (query) { - Map parameters = parseQuery(query) - - if (parameters.containsKey('lineNumber')) { - def lineNumber = parameters.get('lineNumber') - if (lineNumber.isNumber()) { - goToLineNumber(lineNumber.toInteger()) - return true - } - } else if (parameters.containsKey('position')) { - def position = parameters.get('position') - if (position.isNumber()) { - int pos = position.toInteger() - if (textArea.document.length > pos) { - setCaretPositionAndCenter(new DocumentRange(pos, pos)) - return true - } - } - } else if (parameters.containsKey('highlightFlags')) { - def highlightFlags = parameters.get('highlightFlags') - - if ((highlightFlags.indexOf('s') != -1) && parameters.containsKey('highlightPattern')) { - textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR - textArea.caretPosition = 0 - - // Highlight all - def searchFor = createRegExp(parameters.get('highlightPattern')) - def context = newSearchContext(searchFor, true, false, true, true) - def result = SearchEngine.find(textArea, context) - - if (result.matchRange) { - textArea.caretPosition = result.matchRange.startOffset - } - - return true - } - } - } - - return false - } - - protected Map parseQuery(String query) { - Map parameters = [:] - - // Parse parameters - for (def param : query.split('&')) { - int index = param.indexOf('=') - - if (index == -1) { - parameters.put(URLDecoder.decode(param, 'UTF-8'), '') - } else { - def key = param.substring(0, index) - def value = param.substring(index+1) - parameters.put(URLDecoder.decode(key, 'UTF-8'), URLDecoder.decode(value, 'UTF-8')) - } - } - - return parameters - } - - /** - * Create a simple regular expression - * - * Rules: - * '*' matchTypeEntries 0 ou N characters - * '?' matchTypeEntries 1 character - */ - static String createRegExp(String pattern) { - int patternLength = pattern.length() - def sbPattern = new StringBuffer(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 --- // - void preferencesChanged(Map preferences) { - def fontSize = preferences.get(FONT_SIZE_KEY) - - if (fontSize) { - try { - textArea.font = textArea.font.deriveFont(Float.parseFloat(fontSize)) - } catch (Exception ignore) { - } - } - - this.preferences = preferences - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/ClassFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/ClassFilePage.groovy deleted file mode 100644 index f564d89d..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/ClassFilePage.groovy +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import jd.core.Decompiler -import jd.core.loader.Loader -import jd.core.loader.LoaderException -import jd.core.process.DecompilerImpl -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -import org.jd.gui.util.decompiler.ClassFileSourcePrinter -import org.jd.gui.util.decompiler.ContainerLoader -import org.jd.gui.util.decompiler.GuiPreferences -import org.fife.ui.rsyntaxtextarea.DocumentRange -import org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import javax.swing.text.DefaultCaret -import java.awt.Color - -class ClassFilePage extends TypePage { - - protected static final String ESCAPE_UNICODE_CHARACTERS = 'ClassFileViewerPreferences.escapeUnicodeCharacters' - protected static final String OMIT_THIS_PREFIX = 'ClassFileViewerPreferences.omitThisPrefix' - protected static final String REALIGN_LINE_NUMBERS = 'ClassFileViewerPreferences.realignLineNumbers' - protected static final String DISPLAY_DEFAULT_CONSTRUCTOR = 'ClassFileViewerPreferences.displayDefaultConstructor' - - protected static final Decompiler DECOMPILER = new DecompilerImpl() - - protected int maximumLineNumber = -1 - - static { - // Early class loading - def internalTypeName = ClassFilePage.class.name.replace('.', '/') - def preferences = new GuiPreferences() - def loader = new Loader() { - DataInputStream load(String internalTypePath) throws LoaderException { - return new DataInputStream(ClassFilePage.class.classLoader.getResourceAsStream(internalTypeName + '.class')) - } - boolean canLoad(String internalTypePath) { false } - } - def printer = new ClassFileSourcePrinter() { - boolean getRealignmentLineNumber() { false } - boolean isShowPrefixThis() { false } - boolean isUnicodeEscape() { false } - void append(char c) {} - void append(String s) {} - } - DECOMPILER.decompile(preferences, loader, printer, internalTypeName) - } - - ClassFilePage(API api, Container.Entry entry) { - super(api, entry) - // Init view - errorForeground = Color.decode(api.preferences.get('JdGuiPreferences.errorBackgroundColor')) - // Display source - decompile(api.preferences) - } - - void decompile(Map preferences) { - try { - // Clear ... - clearHyperlinks() - clearLineNumbers() - declarations.clear() - typeDeclarations.clear() - strings.clear() - // Init preferences - def p = new GuiPreferences() - p.setUnicodeEscape(getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false)) - p.setShowPrefixThis(! getPreferenceValue(preferences, OMIT_THIS_PREFIX, false)); - p.setShowDefaultConstructor(getPreferenceValue(preferences, DISPLAY_DEFAULT_CONSTRUCTOR, false)) - p.setRealignmentLineNumber(getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false)) - - setShowMisalignment(p.realignmentLineNumber) - // Init loader - def loader = new ContainerLoader(entry) - // Init printer - def printer = new Printer(p) - // Decompile class file - DECOMPILER.decompile(p, loader, printer, entry.path) - - setText(printer.toString()) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } catch (Exception ignore) { - setText('// INTERNAL ERROR //') - } - - maximumLineNumber = getMaximumSourceLineNumber() - } - - @CompileStatic - protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { - String v = preferences.get(key); - - if (v == null) { - return defaultValue; - } else { - return Boolean.valueOf(v); - } - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_JAVA } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('.') - return path.substring(0, index) + '.java' - } - - // --- LineNumberNavigable --- // - int getMaximumLineNumber() { maximumLineNumber } - - void goToLineNumber(int lineNumber) { - int textAreaLineNumber = getTextAreaLineNumber(lineNumber) - if (textAreaLineNumber > 0) { - int start = textArea.getLineStartOffset(textAreaLineNumber-1) - int end = textArea.getLineEndOffset(textAreaLineNumber-1) - setCaretPositionAndCenter(new DocumentRange(start, end)) - } - } - - boolean checkLineNumber(int lineNumber) { lineNumber <= maximumLineNumber } - - // --- PreferencesChangeListener --- // - void preferencesChanged(Map preferences) { - def caret = textArea.caret - int updatePolicy = caret.updatePolicy - - caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); - decompile(preferences) - caret.setUpdatePolicy(updatePolicy) - - super.preferencesChanged(preferences) - } - - @CompileStatic - class Printer extends ClassFileSourcePrinter { - protected StringBuffer stringBuffer = new StringBuffer(10*1024) - protected boolean realignmentLineNumber - protected boolean showPrefixThis - protected boolean unicodeEscape - protected HashMap referencesCache = new HashMap<>() - - Printer(GuiPreferences preferences) { - this.realignmentLineNumber = preferences.getRealignmentLineNumber() - this.showPrefixThis = preferences.isShowPrefixThis() - this.unicodeEscape = preferences.isUnicodeEscape() - } - - boolean getRealignmentLineNumber() { realignmentLineNumber } - boolean isShowPrefixThis() { showPrefixThis } - boolean isUnicodeEscape() { unicodeEscape } - - void append(char c) { stringBuffer.append(c) } - void append(String s) { stringBuffer.append(s) } - - // Manage line number and misalignment - int textAreaLineNumber = 1 - - void start(int maxLineNumber, int majorVersion, int minorVersion) { - super.start(maxLineNumber, majorVersion, minorVersion) - - if (maxLineNumber == 0) { - scrollPane.lineNumbersEnabled = false - } else { - setMaxLineNumber(maxLineNumber) - } - } - void startOfLine(int sourceLineNumber) { - super.startOfLine(sourceLineNumber) - setLineNumber(textAreaLineNumber, sourceLineNumber) - } - void endOfLine() { - super.endOfLine() - textAreaLineNumber++ - } - void extraLine(int count) { - super.extraLine(count) - if (realignmentLineNumber) { - textAreaLineNumber += count - } - } - - // --- Add strings --- // - void printString(String s, String scopeInternalName) { - strings.add(new TypePage.StringData(stringBuffer.length(), s.length(), s, scopeInternalName)) - super.printString(s, scopeInternalName) - } - - // --- Add references --- // - void printTypeImport(String internalName, String name) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, null))) - super.printTypeImport(internalName, name) - } - - void printType(String internalName, String name, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, scopeInternalName))) - super.printType(internalName, name, scopeInternalName) - } - - void printField(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) - super.printField(internalName, name, descriptor, scopeInternalName) - } - void printStaticField(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) - super.printStaticField(internalName, name, descriptor, scopeInternalName) - } - - void printConstructor(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, "", descriptor, scopeInternalName))) - super.printConstructor(internalName, name, descriptor, scopeInternalName) - } - - void printMethod(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) - super.printMethod(internalName, name, descriptor, scopeInternalName) - } - void printStaticMethod(String internalName, String name, String descriptor, String scopeInternalName) { - addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))) - super.printStaticMethod(internalName, name, descriptor, scopeInternalName) - } - - TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { - def key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName - def 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 declarations --- // - void printTypeDeclaration(String internalName, String name) { - def data = new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, null, null) - declarations.put(internalName, data) - typeDeclarations.put(stringBuffer.length(), data) - super.printTypeDeclaration(internalName, name) - } - - void printFieldDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) - super.printFieldDeclaration(internalName, name, descriptor) - } - void printStaticFieldDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) - super.printStaticFieldDeclaration(internalName, name, descriptor) - } - - void printConstructorDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '--' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, "", descriptor)) - super.printConstructorDeclaration(internalName, name, descriptor) - } - void printStaticConstructorDeclaration(String internalName, String name) { - declarations.put(internalName + '--()V', new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, "", '()V')) - super.printStaticConstructorDeclaration(internalName, name) - } - - void printMethodDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) - super.printMethodDeclaration(internalName, name, descriptor) - } - void printStaticMethodDeclaration(String internalName, String name, String descriptor) { - declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)) - super.printStaticMethodDeclaration(internalName, name, descriptor) - } - - String toString() { stringBuffer.toString() } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/CustomLineNumbersPage.groovy b/services/src/main/groovy/org/jd/gui/view/component/CustomLineNumbersPage.groovy deleted file mode 100644 index a2c7645b..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/CustomLineNumbersPage.groovy +++ /dev/null @@ -1,329 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.Token -import org.fife.ui.rsyntaxtextarea.folding.Fold -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.JComponent -import javax.swing.text.EditorKit -import javax.swing.text.JTextComponent -import java.awt.Color -import java.awt.Dimension -import java.awt.Font -import java.awt.FontMetrics -import java.awt.Graphics -import java.awt.Graphics2D -import java.awt.Insets -import java.awt.Rectangle - -@CompileStatic -abstract class CustomLineNumbersPage extends HyperlinkPage { - - Color errorForeground = Color.RED - boolean showMisalignment = true - - void setErrorForeground(Color color) { - errorForeground = color - } - - void setShowMisalignment(boolean b) { - showMisalignment = b - } - - /** - * Map[textarea line number] = original line number - */ - protected int[] lineNumberMap = null - protected int maxLineNumber = 0 - - void setMaxLineNumber(int maxLineNumber) { - if (maxLineNumber > 0) { - if (lineNumberMap == null) { - lineNumberMap = new int[maxLineNumber * 3 / 2] - } else if (lineNumberMap.length <= maxLineNumber) { - int[] tmp = new int[maxLineNumber * 3 / 2] - System.arraycopy(lineNumberMap, 0, tmp, 0, lineNumberMap.length) - lineNumberMap = tmp - } - - if (this.maxLineNumber < maxLineNumber) { - this.maxLineNumber = maxLineNumber - } - } - } - - void setLineNumber(int textAreaLineNumber, int originalLineNumber) { - if (originalLineNumber > 0) { - setMaxLineNumber(textAreaLineNumber) - lineNumberMap[textAreaLineNumber] = originalLineNumber - } - } - - void clearLineNumbers() { - if (lineNumberMap) { - Arrays.fill(lineNumberMap, 0) - } - } - - int getMaximumSourceLineNumber() { maxLineNumber } - - @CompileStatic - 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 newRSyntaxTextArea() { new SourceSyntaxTextArea() } - - @CompileStatic - class SourceSyntaxTextArea extends RSyntaxTextArea { - /** - * @see HyperlinkPage.HyperlinkSyntaxTextArea#getUnderlineForToken(org.fife.ui.rsyntaxtextarea.Token) - */ - @Override - boolean getUnderlineForToken(Token t) { - def entry = hyperlinks.floorEntry(t.offset) - if (entry) { - def data = entry.value - if (data && (t.offset < data.endPosition) && (t.offset >= data.startPosition) && isHyperlinkEnabled(data)) { - return true - } - } - return super.getUnderlineForToken(t) - } - - @Override protected RTextAreaUI createRTextAreaUI() { new SourceSyntaxTextAreaUI(this) } - } - - /** - * A lot of code to replace the default LineNumberList... - */ - @CompileStatic - class SourceSyntaxTextAreaUI extends RSyntaxTextAreaUI { - SourceSyntaxTextAreaUI(JComponent rSyntaxTextArea) { super(rSyntaxTextArea) } - @Override EditorKit getEditorKit(JTextComponent tc) { new SourceSyntaxTextAreaEditorKit() } - @Override Rectangle getVisibleEditorRect() { super.getVisibleEditorRect() } - } - - @CompileStatic - class SourceSyntaxTextAreaEditorKit extends RSyntaxTextAreaEditorKit { - @Override LineNumberList createLineNumberList(RTextArea textArea) { new SourceLineNumberList(textArea) } - } - - /** - * Why 'LineNumberList' is so unexpandable ? Too many private fields & methods and too many package scope. - */ - @CompileStatic - class SourceLineNumberList extends LineNumberList { - protected RTextArea rTextArea - protected Map aaHints - protected Rectangle visibleRect - protected Insets textAreaInsets - protected Dimension preferredSize - - 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.lineHeight - int ascent = rTextArea.maxAscent - def fm = ((RSyntaxTextArea)rTextArea).foldManager - int RHS_BORDER_WIDTH = rhsBorderWidth - def metrics = g.fontMetrics - int rhs = width - RHS_BORDER_WIDTH - - if (parent instanceof Gutter) { // Should always be true - g.setColor(parent.background) - } else { - g.setColor(background) - } - - g.fillRect(0, visibleRect.@y, cellWidth, visibleRect.@height) - g.setFont(font) - - if (aaHints) { - ((Graphics2D)g).addRenderingHints(aaHints) - } - - if (rTextArea.lineWrap) { - def ui = (SourceSyntaxTextAreaUI)rTextArea.getUI() - def v = ui.getRootView(rTextArea).getView(0) - def root = rTextArea.document.defaultRootElement - int lineCount = root.elementCount - int topPosition = rTextArea.viewToModel(visibleRect.location) - int topLine = root.getElementIndex(topPosition) - def visibleEditorRect = ui.visibleEditorRect - def 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) { - Fold fold = fm.getFoldForLine(topLine) - if (fold?.isCollapsed()) { - topLine += fold.collapsedLineCount - } - } - - 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 = (int) ((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(foreground) - - int line = topLine + 1 - - while ((y < visibleRect.@y + visibleRect.@height + ascent) && (line <= rTextArea.lineCount)) { - 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?.isCollapsed()) { - int hiddenLineCount = fold.lineCount - 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) { - 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 : foreground) - g.drawString(number, x-strWidth, y) - } - } - - int getRhsBorderWidth() { ((RSyntaxTextArea)rTextArea).isCodeFoldingEnabled() ? 0 : 4 } - - @Override - 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 as int - count++ - } - - int preferredWidth = fontMetrics.charWidth('9' as char) * count + 10 - preferredSize = new Dimension(preferredWidth, 0) - } else { - preferredSize = new Dimension(0, 0) - } - } - - return preferredSize - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/EjbJarXmlFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/EjbJarXmlFilePage.groovy deleted file mode 100644 index c5f36fc0..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/EjbJarXmlFilePage.groovy +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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.xml.AbstractXmlPathFinder -import org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import java.awt.* - -class EjbJarXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - EjbJarXmlFilePage(API api, Container.Entry entry) { - this.api = api - this.entry = entry - // Load content file - def text = entry.inputStream.text - // Create hyperlinks - new PathFinder().find(text) - // Display - setText(text) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_XML } - - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - def internalTypeName = hyperlinkData.internalTypeName - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, hyperlinkData.internalTypeName) - } else if (entries) { - api.openURI(x, y, entries, null, hyperlinkData.internalTypeName) - } - } - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def entry : hyperlinks.entrySet()) { - def data = entry.value - def internalTypeName = data.internalTypeName - boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - - if (data.enabled != enabled) { - data.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - class PathFinder extends AbstractXmlPathFinder { - static HashSet typeHyperlinkPaths = [ - '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' - ] - - PathFinder() { - super(typeHyperlinkPaths) - } - - void handle(String path, String text, int position) { - def trim = text.trim() - if (trim) { - int startIndex = position + text.indexOf(trim) - int endIndex = startIndex + trim.length() - def internalTypeName = trim.replace('.', '/') - addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/HyperlinkPage.groovy b/services/src/main/groovy/org/jd/gui/view/component/HyperlinkPage.groovy deleted file mode 100644 index de834b1c..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/HyperlinkPage.groovy +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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 - -@CompileStatic -abstract class HyperlinkPage extends TextPage { - protected static final Cursor DEFAULT_CURSOR = Cursor.defaultCursor - protected static final Cursor HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) - - protected TreeMap hyperlinks = new TreeMap<>() - - HyperlinkPage() { - def listener = new MouseAdapter() { - int lastX = -1 - int lastY = -1 - int lastModifiers = -1 - - void mouseClicked(MouseEvent e) { - if ((e.clickCount == 1) && ((e.modifiers & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0)) { - int offset = textArea.viewToModel(new Point(e.x, e.y)) - if (offset != -1) { - def entry = hyperlinks.floorEntry(offset) - if (entry) { - def entryData = entry.value - if (entryData && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { - openHyperlink(e.getXOnScreen(), e.getYOnScreen(), entryData) - } - } - } - } - } - - void mouseMoved(MouseEvent e) { - if ((e.x != lastX) || (e.y != lastY) || (lastModifiers != e.modifiers)) { - lastX = e.x - lastY = e.y - lastModifiers = e.modifiers - - if ((e.modifiers & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0) { - int offset = textArea.viewToModel(new Point(e.x, e.y)) - if (offset != -1) { - def entry = hyperlinks.floorEntry(offset) - if (entry) { - def entryData = entry.value - if (entryData && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { - if (textArea.cursor != HAND_CURSOR) { - textArea.cursor = HAND_CURSOR - } - return - } - } - } - } - - if (textArea.cursor!= DEFAULT_CURSOR) { - textArea.cursor = DEFAULT_CURSOR - } - } - } - } - - textArea.addMouseListener(listener) - textArea.addMouseMotionListener(listener) - } - - protected RSyntaxTextArea newRSyntaxTextArea() { new HyperlinkSyntaxTextArea() } - - void addHyperlink(HyperlinkData hyperlinkData) { - hyperlinks.put(hyperlinkData.startPosition, hyperlinkData) - } - - void clearHyperlinks() { - hyperlinks.clear() - } - - protected abstract boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) - - protected abstract void openHyperlink(int x, int y, HyperlinkData hyperlinkData) - - static class HyperlinkData { - int startPosition - int endPosition - - HyperlinkData(int startPosition, int endPosition) { - this.startPosition = startPosition - this.endPosition = endPosition - } - } - - class HyperlinkSyntaxTextArea extends RSyntaxTextArea { - boolean getUnderlineForToken(Token t) { - def entry = hyperlinks.floorEntry(t.offset) - if (entry) { - def entryData = entry.value - if (entryData && (t.offset < entryData.endPosition) && (t.offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) { - return true - } - } - return super.getUnderlineForToken(t) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/JavaFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/JavaFilePage.groovy deleted file mode 100644 index 81997f08..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/JavaFilePage.groovy +++ /dev/null @@ -1,737 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.jd.gui.api.API -import org.jd.gui.api.model.Container -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 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.fife.ui.rsyntaxtextarea.SyntaxConstants - -class JavaFilePage extends TypePage { - - JavaFilePage(API api, Container.Entry entry) { - super(api, entry) - // Load content file - def text = entry.inputStream.text.replace('\r\n', '\n').replace('\r', '\n') - // Parse - def declarationListener = new DeclarationListener(entry) - def referenceListener = new ReferenceListener(entry) - - ANTLRJavaParser.parse(new ANTLRInputStream(text), declarationListener) - referenceListener.init(declarationListener) - ANTLRJavaParser.parse(new ANTLRInputStream(text), referenceListener) - // Display - setText(text) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_JAVA } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - @CompileStatic - class DeclarationListener extends AbstractJavaListener { - - protected StringBuffer sbTypeDeclaration = new StringBuffer() - protected String currentInternalTypeName - - DeclarationListener(Container.Entry entry) { super(entry) } - - HashMap getNameToInternalTypeName() { super.nameToInternalTypeName } - - // --- Add declarations --- // - void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { - super.enterPackageDeclaration(ctx); - - if (! packageName.isEmpty()) { - sbTypeDeclaration.append(packageName).append('/'); - } - } - - void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { - List identifiers = ctx.qualifiedName().Identifier() - String internalTypeName = concatIdentifiers(identifiers) - String typeName = identifiers.get(identifiers.size()-1).symbol.text - - nameToInternalTypeName.put(typeName, internalTypeName) - } - - void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterTypeDeclaration(ParserRuleContext ctx) { - // Type declaration - def identifier = ctx.getToken(JavaParser.Identifier, 0); - def typeName = identifier.text - int position = identifier.symbol.startIndex - 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 ? resolveInternalTypeName(superType.classOrInterfaceType().Identifier()) : null - - def data = new TypeDeclarationData(position, typeName.length(), currentInternalTypeName, null, null, superInternalTypeName) - - declarations.put(currentInternalTypeName, data) - typeDeclarations.put(position, data) - } - - 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) { - def first = ctx.getChild(0); - - if (first instanceof TerminalNode) { - if (first.getSymbol().type == JavaParser.STATIC) { - String name = first.text - int position = first.getSymbol().startIndex - declarations.put(currentInternalTypeName + '--()V', new TypePage.DeclarationData(position, 6, currentInternalTypeName, name, '()V')) - } - } - } - } - - void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) { - def typeContext = ctx.type(); - - for (def constantDeclaratorContext : ctx.constantDeclarator()) { - def identifier = constantDeclaratorContext.Identifier() - def name = identifier.text - int dimensionOnVariable = countDimension(constantDeclaratorContext.children) - def descriptor = createDescriptor(typeContext, dimensionOnVariable) - int position = identifier.symbol.startIndex - - declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) - } - } - - void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) { - def typeContext = ctx.type(); - - for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) { - def variableDeclaratorId = declaration.variableDeclaratorId() - def identifier = variableDeclaratorId.Identifier() - def name = identifier.text - int dimensionOnVariable = countDimension(variableDeclaratorId.children) - def descriptor = createDescriptor(typeContext, dimensionOnVariable) - int position = identifier.symbol.startIndex - def data = new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor) - - declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, data) - } - } - - void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { - enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); - } - - void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) { - enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type()); - } - - void enterMethodDeclaration( - ParserRuleContext ctx, TerminalNode identifier, - JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) { - - def name = identifier.text - def paramDescriptors = createParamDescriptors(formalParameters.formalParameterList()) - def returnDescriptor = createDescriptor(returnType, 0) - def descriptor = paramDescriptors + returnDescriptor - int position = identifier.symbol.startIndex - - declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) - } - - void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) { - def identifier = ctx.Identifier() - def name = identifier.text - def paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList()) - def descriptor = paramDescriptors + "V" - int position = identifier.symbol.startIndex - - declarations.put(currentInternalTypeName + '--' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor)) - } - - String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { - StringBuffer paramDescriptors = null - - if (formalParameterList != null) { - def formalParameters = formalParameterList.formalParameter() - paramDescriptors = new StringBuffer("(") - - for (def formalParameter : formalParameters) { - int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children) - def descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter) - - paramDescriptors.append(descriptor) - } - } - - return (paramDescriptors == null) ? "()" : paramDescriptors.append(')').toString(); - } - } - - @CompileStatic - class ReferenceListener extends AbstractJavaListener { - - protected StringBuffer sbTypeDeclaration = new StringBuffer() - protected HashMap referencesCache = new HashMap<>() - protected String currentInternalTypeName - protected Context currentContext = null - - ReferenceListener(Container.Entry entry) { super(entry) } - - void init(DeclarationListener declarationListener) { - this.nameToInternalTypeName.putAll(declarationListener.nameToInternalTypeName) - } - - // --- Add declarations --- // - void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) { - super.enterPackageDeclaration(ctx); - - if (! packageName.isEmpty()) { - sbTypeDeclaration.append(packageName).append('/'); - } - } - - void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { - List identifiers = ctx.qualifiedName().Identifier() - int position = identifiers.get(0).symbol.startIndex - String internalTypeName = concatIdentifiers(identifiers) - - addHyperlink(new TypePage.HyperlinkReferenceData(position, internalTypeName.length(), newReferenceData(internalTypeName, null, null, null))) - } - - void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); } - void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); } - - void enterTypeDeclaration(ParserRuleContext ctx) { - // Type declaration - def identifier = ctx.getToken(JavaParser.Identifier, 0); - def typeName = identifier.text - 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) - } - - 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() - } - - void enterFormalParameters(JavaParser.FormalParametersContext ctx) { - def formalParameterList = ctx.formalParameterList() - - if (formalParameterList != null) { - def formalParameters = formalParameterList.formalParameter() - - for (def formalParameter : formalParameters) { - int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children) - def descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter) - def name = formalParameter.variableDeclaratorId().Identifier().symbol.text - - currentContext.nameToDescriptor.put(name, descriptor) - } - } - } - - // --- Add references --- // - void enterType(JavaParser.TypeContext ctx) { - // Add type reference - def classOrInterfaceType = ctx.classOrInterfaceType() - - if (classOrInterfaceType != null) { - def identifiers = classOrInterfaceType.Identifier() - def name = concatIdentifiers(identifiers) - def internalTypeName = resolveInternalTypeName(identifiers) - int position = identifiers.get(0).symbol.startIndex - - addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName))) - } - } - - void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) { - def typeContext = ctx.type() - - for (def variableDeclarator : ctx.variableDeclarators().variableDeclarator()) { - def variableDeclaratorId = variableDeclarator.variableDeclaratorId() - int dimensionOnVariable = countDimension(variableDeclaratorId.children) - def descriptor = createDescriptor(typeContext, dimensionOnVariable) - def name = variableDeclarator.variableDeclaratorId().Identifier().getSymbol().getText() - - currentContext.nameToDescriptor.put(name, descriptor) - } - } - - void enterCreator(JavaParser.CreatorContext ctx) { - enterNewExpression(ctx.createdName().Identifier(), ctx.classCreatorRest()) - } - - void enterInnerCreator(JavaParser.InnerCreatorContext ctx) { - enterNewExpression(Collections.singletonList(ctx.Identifier()), ctx.classCreatorRest()) - } - - void enterNewExpression(List identifiers, JavaParser.ClassCreatorRestContext classCreatorRest) { - if (identifiers.size() > 0) { - def name = concatIdentifiers(identifiers) - def internalTypeName = resolveInternalTypeName(identifiers) - int position = identifiers.get(0).symbol.startIndex - - if (classCreatorRest) { - // Constructor call -> Add a link to the constructor declaration - def expressionList = classCreatorRest.arguments().expressionList() - def descriptor = expressionList ? 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))) - } - } - } - - void enterExpression(JavaParser.ExpressionContext ctx) { - switch (ctx.getChildCount()) { - case 1: - TerminalNode identifier0 = getToken(ctx.children, JavaParser.Identifier, 0); - - if (identifier0 != null) { - if (isAField(ctx)) { - def primaryContext = ctx.primary() - - if (primaryContext) { - String fieldName = primaryContext.literal().StringLiteral(); - - if (!currentContext.getDescriptor(fieldName) != null) { - // Not a local variable or a method parameter - def fieldTypeName = searchInternalTypeNameForThisFieldName(currentInternalTypeName, fieldName) - int position = ctx.Identifier().getSymbol().startIndex - - addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, '?', currentInternalTypeName))) - } - } - } - } else { - def identifier = ctx.primary().Identifier() - - if (identifier) { - def symbol = identifier.getSymbol() - def name = symbol.text - def internalTypeName = nameToInternalTypeName.get(name) - - if (internalTypeName) { - int position = symbol.startIndex - - 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 - def identifier3 = getToken(ctx.children, JavaParser.Identifier, 2); - - if ((identifier3 != null) && isAField(ctx)) { - def fieldTypeName = getInternalTypeName(ctx.getChild(0)) - - if (fieldTypeName) { - int position = identifier3.symbol.startIndex - def 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) { - def expressionListContext = ctx.expressionList(); - - if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) { - enterCallMethodExpression(ctx, expressionListContext) - } - } - } - break; - } - } - - void enterCallMethodExpression(JavaParser.ExpressionContext ctx, JavaParser.ExpressionListContext expressionListContext) { - ParseTree first = ctx.children.get(0) - - if (first instanceof JavaParser.ExpressionContext) { - switch (first.getChildCount()) { - case 1: - def primary = first.primary() - def identifier = primary.Identifier() - - if (identifier) { - def symbol = identifier.getSymbol() - - if (symbol) { - String methodName = symbol.text - String methodTypeName = searchInternalTypeNameForThisMethodName(currentInternalTypeName, methodName) - - if (methodTypeName) { - int position = symbol.startIndex - def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' - - addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))) - } - } - } else { - def symbol = primary.getChild(TerminalNode.class, 0).getSymbol() - - if (symbol) { - switch (symbol.type) { - case JavaParser.THIS: - int position = symbol.startIndex - def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' - - addHyperlink(new TypePage.HyperlinkReferenceData(position, 4, newReferenceData(currentInternalTypeName, '', methodDescriptor, currentInternalTypeName))) - break - case JavaParser.SUPER: - def data = declarations.get(currentInternalTypeName) - - if (data instanceof TypeDeclarationData) { - int position = symbol.startIndex - def methodTypeName = ((TypeDeclarationData) data).superTypeName - def methodDescriptor = expressionListContext ? 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) && (dot.getSymbol().getType() == JavaParser.DOT)) { - ParseTree identifier3 = first.getChild(2) - - if ((identifier3 instanceof TerminalNode) && (identifier3.getSymbol().type == JavaParser.Identifier)) { - String methodTypeName = getInternalTypeName(first.getChild(0)) - - if (methodTypeName) { - int position = identifier3.getSymbol().startIndex - def methodName = identifier3.getText() - def methodDescriptor = expressionListContext ? getParametersDescriptor(expressionListContext).append('?').toString() : '()?' - - addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName))) - } - } - } - break - } - } - } - - StringBuffer getParametersDescriptor(JavaParser.ExpressionListContext expressionListContext) { - def sb = new StringBuffer('(') - for (def exp : expressionListContext.expression()) sb.append('?') - sb.append(')') - return sb - } - - boolean isAField(JavaParser.ExpressionContext ctx) { - def parent = ctx.parent - - if (parent instanceof JavaParser.ExpressionContext) { - int size = parent.getChildCount(); - - if (parent.getChild(size - 1) != ctx) { - for (int i=0; i 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; - } - - void enterBlock(JavaParser.BlockContext ctx) { - currentContext = new Context(currentContext) - } - - void exitBlock(JavaParser.BlockContext ctx) { - currentContext = currentContext.outerContext - } - - TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) { - def key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName - def 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 --- // - void enterLiteral(JavaParser.LiteralContext ctx) { - def 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)) - } - } - } - - @CompileStatic - static class Context { - Context outerContext - - HashMap nameToDescriptor = new HashMap<>() - - Context(Context outerContext) { - this.outerContext = outerContext - } - - /** - * @param name Parameter or variable name - * @return Qualified type name - */ - String getDescriptor(String name) { - String descriptor = nameToDescriptor.get(name) - - if ((descriptor == null) && (outerContext != null)) { - descriptor = outerContext.getDescriptor(name) - } - - return descriptor - } - } - - @CompileStatic - static class TypeDeclarationData extends TypePage.DeclarationData { - String superTypeName - - TypeDeclarationData(int startPosition, int length, String type, String name, String descriptor, String superTypeName) { - super(startPosition, length, type, name, descriptor) - - this.superTypeName = superTypeName - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/LogPage.groovy b/services/src/main/groovy/org/jd/gui/view/component/LogPage.groovy deleted file mode 100644 index cf5fdc84..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/LogPage.groovy +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.Indexes - -import java.awt.Point - -class LogPage extends HyperlinkPage implements UriGettable, IndexesChangeListener { - protected API api - protected URI uri - protected Collection collectionOfIndexes - - 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.size()) - // Display - setText(content) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - 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(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - def text = getText() - def typeAndMethodNames = text.substring(hyperlinkData.startPosition, hyperlinkData.endPosition) - int lastDotIndex = typeAndMethodNames.lastIndexOf('.') - def methodName = typeAndMethodNames.substring(lastDotIndex+1) - def internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/') - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - - int leftParenthesisIndex = hyperlinkData.endPosition + 1 - int rightParenthesisIndex = text.indexOf(')', leftParenthesisIndex) - def lineNumberOrNativeMethodFlag = text.substring(leftParenthesisIndex, rightParenthesisIndex) - - if (lineNumberOrNativeMethodFlag.equals('Native Method')) { - // Example: at java.security.AccessController.doPrivileged(Native Method) - lastDotIndex = internalTypeName.lastIndexOf('/') - def 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(':') - def lineNumber = lineNumberOrNativeMethodFlag.substring(colonIndex+1) - api.openURI(x, y, entries, 'lineNumber=' + lineNumber, null) - } - } - } - - // --- UriGettable --- // - URI getUri() { uri } - - // --- ContentSavable --- // - String getFileName() { - def path = uri.path - int index = path.lastIndexOf('/') - return path.substring(index + 1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - def text = getText() - - for (def entry : hyperlinks.entrySet()) { - def entryData = entry.value as LogHyperlinkData - def typeAndMethodNames = text.substring(entryData.startPosition, entryData.endPosition) - int lastDotIndex = typeAndMethodNames.lastIndexOf('.') - def internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/') - boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - - if (entryData.enabled != enabled) { - entryData.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - static class LogHyperlinkData extends HyperlinkPage.HyperlinkData { - boolean enabled = false - - LogHyperlinkData(int startPosition, int endPosition) { - super(startPosition, endPosition) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/ManifestFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/ManifestFilePage.groovy deleted file mode 100644 index 695f0f6c..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/ManifestFilePage.groovy +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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 java.awt.Point - -class ManifestFilePage extends HyperlinkPage implements UriGettable, IndexesChangeListener { - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - ManifestFilePage(API api, Container.Entry entry) { - this.api = api - this.entry = entry - // Load content file - def text = entry.inputStream.text - // 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:'.size()) - int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex) - def typeName = text.substring(startIndex, endIndex) - def 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:'.size()) - int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex) - def typeName = text.substring(startIndex, endIndex) - def 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) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - @CompileStatic - int skipSeparators(String text, int index) { - int length = text.size() - - while (index < length) { - switch (text.charAt(index)) { - case ' ': case '\t': case '\n': case '\r': - index++ - break - default: - return index - } - } - - return index - } - - @CompileStatic - int searchEndIndexOfValue(String text, int startLineIndex, int startIndex) { - int length = text.size() - 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(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - // Open link - def text = getText() - def textLink = getValue(text, hyperlinkData.startPosition, hyperlinkData.endPosition) - def internalTypeName = textLink.replace('.', '/') - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, hyperlinkData.fragment) - } else if (entries) { - api.openURI(x, y, entries, null, hyperlinkData.fragment) - } - } - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - def text = getText() - - for (def entry : hyperlinks.entrySet()) { - def entryData = entry.value as ManifestHyperlinkData - def textLink = getValue(text, entryData.startPosition, entryData.endPosition) - def internalTypeName = textLink.replace('.', '/') - boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - - if (entryData.enabled != enabled) { - entryData.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - 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 ', '') - } - - static class ManifestHyperlinkData extends HyperlinkPage.HyperlinkData { - boolean enabled - String fragment - - ManifestHyperlinkData(int startPosition, int endPosition, String fragment) { - super(startPosition, endPosition) - this.enabled = false - this.fragment = fragment - } - } -} - diff --git a/services/src/main/groovy/org/jd/gui/view/component/OneTypeReferenceByLinePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/OneTypeReferenceByLinePage.groovy deleted file mode 100644 index 7fb4f3a3..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/OneTypeReferenceByLinePage.groovy +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 java.awt.Point - -class OneTypeReferenceByLinePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - OneTypeReferenceByLinePage(API api, Container.Entry entry) { - this.api = api - this.entry = entry - // Load content file - def text = entry.inputStream.text.replace('\r\n', '\n').replace('\r', '\n') - // Create hyperlinks - int offset = 0 - - text.eachLine { String line -> - def trim = line.trim() - - if (trim) { - int startIndex = offset + line.indexOf(trim) - int endIndex = startIndex + trim.length() - def internalTypeName = trim.replace('.', '/') - - addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) - } - - offset += line.length() + 1 - } - // Display - setText(text) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - def internalTypeName = hyperlinkData.internalTypeName - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, hyperlinkData.internalTypeName) - } else if (entries) { - api.openURI(x, y, entries, null, hyperlinkData.internalTypeName) - } - } - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def entry : hyperlinks.entrySet()) { - def data = entry.value - def internalTypeName = data.internalTypeName - boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - - if (data.enabled != enabled) { - data.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/TextPage.groovy b/services/src/main/groovy/org/jd/gui/view/component/TextPage.groovy deleted file mode 100644 index c194d496..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/TextPage.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.io.NewlineOutputStream - -import java.awt.datatransfer.StringSelection - -@CompileStatic -class TextPage extends AbstractTextPage implements ContentCopyable, ContentSelectable, ContentSavable { - - // --- ContentCopyable --- // - void copy() { - if (textArea.selectionStart == textArea.selectionEnd) { - toolkit.systemClipboard.setContents(new StringSelection(''), null) - } else { - textArea.copyAsRtf() - } - } - - // --- ContentSelectable --- // - void selectAll() { - textArea.selectAll() - } - - // --- ContentSavable --- // - String getFileName() { 'file.txt' } - - void save(API api, OutputStream os) { - new NewlineOutputStream(os).withWriter('UTF-8') { Writer w -> - w.write(textArea.text) - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/TypePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/TypePage.groovy deleted file mode 100644 index 83143336..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/TypePage.groovy +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -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.util.matcher.DescriptorMatcher -import org.fife.ui.rsyntaxtextarea.DocumentRange - -import java.awt.Point -import java.util.regex.Pattern - -abstract class TypePage extends CustomLineNumbersPage implements UriGettable, UriOpenable, IndexesChangeListener, FocusedTypeGettable { - - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - protected HashMap declarations = new HashMap<>() - protected TreeMap typeDeclarations = new TreeMap<>() - protected ArrayList references = new ArrayList<>() - protected ArrayList strings = new ArrayList<>() - - TypePage(API api, Container.Entry entry) { - // Init attributes - this.api = api - this.entry = entry - } - - @Override - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.reference.enabled } - - @Override - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.reference.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - ReferenceData reference = hyperlinkData.reference - def typeName = reference.typeName - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it != null } - def fragment = typeName - - if (reference.name) { - fragment += '-' + reference.name - } - if (reference.descriptor) { - fragment += '-' + reference.descriptor - } - - if (entries.contains(entry)) { - api.openURI(new URI(uri.scheme, uri.authority, uri.path, fragment)) - } else { - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, fragment) - } else if (entries) { - api.openURI(x, y, entries, null, fragment) - } - } - } - } - - // --- UriGettable --- // - @Override URI getUri() { entry.uri } - - // --- UriOpenable --- // - /** - * @param uri for URI format, @see jd.gui.api.feature.UriOpenable - */ - @Override - boolean openUri(URI uri) { - List ranges = [] - def fragment = uri.fragment - def query = uri.query - - textArea.highlighter.clearMarkAllHighlights() - - if (fragment) { - matchFragmentAndAddDocumentRange(fragment, declarations, ranges) - } - - if (query) { - Map parameters = parseQuery(query) - - if (parameters.containsKey('lineNumber')) { - def lineNumber = parameters.get('lineNumber') - if (lineNumber.isNumber()) { - goToLineNumber(lineNumber.toInteger()) - return true - } - } else if (parameters.containsKey('position')) { - def position = parameters.get('position') - if (position.isNumber()) { - int pos = position.toInteger() - if (textArea.document.length > pos) { - ranges.add(new DocumentRange(pos, pos)) - } - } - } else { - matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges) - } - } - - if (ranges) { - textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR - textArea.markAll(ranges) - setCaretPositionAndCenter(ranges.sort().get(0)) - } - - return true - } - - @CompileStatic - 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 (def entry : declarations.entrySet()) { - if (entry.key.endsWith(slashAndTypeName) || entry.key.equals(typeName)) { - ranges.add(new DocumentRange(entry.value.startPosition, entry.value.endPosition)) - } - } - } else { - def prefix = fragment.substring(0, lastDash+1) - def suffix = fragment.substring(lastDash+1) - def addRangeClosure - - if (suffix.charAt(0) == '(') { - addRangeClosure = { String key, DeclarationData value -> - int index = key.lastIndexOf('-') + 1 - if (DescriptorMatcher.matchMethodDescriptors(suffix, key.substring(index))) { - ranges.add(new DocumentRange(value.startPosition, value.endPosition)) - } - } - } else { - addRangeClosure = { String key, DeclarationData value -> - int index = key.lastIndexOf('-') + 1 - if (DescriptorMatcher.matchFieldDescriptors(suffix, key.substring(index))) { - ranges.add(new DocumentRange(value.startPosition, value.endPosition)) - } - } - } - - if (fragment.charAt(0) == '*') { - // Unknown type - String slashAndTypeNameAndName = prefix.substring(1) - String typeNameAndName = prefix.substring(2) - - for (def entry : declarations.entrySet()) { - if ((entry.key.indexOf(slashAndTypeNameAndName) != -1) || (entry.key.startsWith(typeNameAndName))) { - addRangeClosure(entry.key, entry.value) - } - } - } else { - // Known type - for (def entry : declarations.entrySet()) { - if (entry.key.startsWith(prefix)) { - addRangeClosure(entry.key, entry.value) - } - } - } - } - } else { - // Known type and descriptor ==> Search and high light item - def data = declarations.get(fragment) - if (data) { - 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)) - } - } - } - - @CompileStatic - static void matchQueryAndAddDocumentRange( - Map parameters, - HashMap declarations, TreeMap hyperlinks, ArrayList strings, - List ranges) { - - def highlightFlags = parameters.get('highlightFlags') - def highlightPattern = parameters.get('highlightPattern') - - if (highlightFlags && highlightPattern) { - def highlightScope = parameters.get('highlightScope') - def regexp = createRegExp(highlightPattern) - def pattern = Pattern.compile(regexp + '.*') - - if (highlightFlags.indexOf('s') != -1) { - // Highlight strings - def patternForString = Pattern.compile(regexp) - - for (def data : strings) { - if (matchScope(highlightScope, data.owner)) { - def 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 (def entry : declarations.entrySet()) { - def declaration = entry.value - - 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 (def entry : hyperlinks.entrySet()) { - def hyperlink = entry.value - def 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) - } - } - } - } - } - } - - @CompileStatic - static boolean matchScope(String scope, String type) { - if (!scope) - return true - if (scope.charAt(0) == '*') - return type.endsWith(scope.substring(1)) || type.equals(scope.substring(2)) - return type.equals(scope) - } - - @CompileStatic - static void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List ranges) { - if (pattern.matcher(text).matches()) { - ranges.add(new DocumentRange(start, end)) - } - } - - @CompileStatic - 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 String getFocusedTypeName() { typeDeclarations.floorEntry(textArea.caretPosition)?.value?.typeName } - - @Override Container.Entry getEntry() { entry } - - // --- IndexesChangeListener --- // - @Override - @CompileStatic - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def reference : references) { - def typeName = reference.typeName - boolean enabled - - if (reference.name) { - try { - // Recursive search - typeName = searchTypeHavingMember(typeName, reference.name, reference.descriptor, entry) - if (typeName) { - // Replace type with the real type having the referenced member - reference.typeName = typeName - enabled = true - } else { - enabled = false - } - } catch (Error ignore) { - // Catch StackOverflowError or OutOfMemoryError - enabled = false - } - } else { - enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(typeName) } != null - } - - if (reference.enabled != enabled) { - reference.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - protected String searchTypeHavingMember(String typeName, String name, String descriptor, Container.Entry entry) { - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(typeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { Container.Entry e -> e.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - return searchTypeHavingMember(typeName, name, descriptor, sameContainerEntries) - } else { - return searchTypeHavingMember(typeName, name, descriptor, entries) - } - } - - @CompileStatic - protected String searchTypeHavingMember(String typeName, String name, String descriptor, List entries) { - for (def entry : entries) { - def type = api.getTypeFactory(entry).make(api, entry, typeName) - - if (type) { - if (descriptor.indexOf('(') == -1) { - // Search a field - for (def field : type.fields) { - if (field.name.equals(name) && DescriptorMatcher.matchFieldDescriptors(field.descriptor, descriptor)) { - // Field found - return typeName - } - } - } else { - // Search a method - for (def method : type.methods) { - if (method.name.equals(name) && DescriptorMatcher.matchMethodDescriptors(method.descriptor, descriptor)) { - // Method found - return typeName - } - } - } - - // Not found -> Search in super type - def typeOwnerName = searchTypeHavingMember(type.superName, name, descriptor, entry) - if (typeOwnerName) { - return typeOwnerName - } - } - } - - return null - } - - @CompileStatic - static class StringData { - int startPosition - int endPosition - String text - String owner - - StringData(int startPosition, int length, String text, String owner) { - this.startPosition = startPosition - this.endPosition = startPosition + length - this.text = text - this.owner = owner - } - } - - @CompileStatic - static class DeclarationData { - int startPosition - int endPosition - String typeName - /** - * Field or method name or null for type - */ - String name - String descriptor - - 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 - } - - boolean isAType() { name == null } - boolean isAField() { descriptor && descriptor.charAt(0) != '('} - boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} - boolean isAConstructor() { "".equals(name) } - } - - @CompileStatic - static class HyperlinkReferenceData extends HyperlinkPage.HyperlinkData { - ReferenceData reference - - HyperlinkReferenceData(int startPosition, int length, ReferenceData reference) { - super(startPosition, startPosition+length) - this.reference = reference - } - } - - @CompileStatic - static class ReferenceData { - String typeName - /** - * Field or method name or null for type - */ - String name - /** - * Field or method descriptor or null for type - */ - 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". - */ - String owner - /** - * "Enabled" flag for link of reference - */ - boolean enabled = false - - ReferenceData(String typeName, String name, String descriptor, String owner) { - this.typeName = typeName - this.name = name - this.descriptor = descriptor - this.owner = owner - } - - boolean isAType() { name == null } - boolean isAField() { descriptor && descriptor.charAt(0) != '('} - boolean isAMethod() { descriptor && descriptor.charAt(0) == '('} - boolean isAConstructor() { "".equals(name) } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/TypeReferencePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/TypeReferencePage.groovy deleted file mode 100644 index 21b5bd32..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/TypeReferencePage.groovy +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 groovy.transform.CompileStatic -import org.fife.ui.rsyntaxtextarea.DocumentRange - -import java.util.regex.Pattern - -/** - * Page containing type references (Hyperlinks to pages of type) - */ -abstract class TypeReferencePage extends HyperlinkPage { - - // --- UriOpenable --- // - boolean openUri(URI uri) { - java.util.List ranges = [] - def query = uri.query - - textArea.highlighter.clearMarkAllHighlights() - - if (query) { - Map parameters = parseQuery(query) - - if (parameters.containsKey('lineNumber')) { - def lineNumber = parameters.get('lineNumber') - if (lineNumber.isNumber()) { - goToLineNumber(lineNumber.toInteger()) - return true - } - } else if (parameters.containsKey('position')) { - def position = parameters.get('position') - if (position.isNumber()) { - int pos = position.toInteger() - if (textArea.document.length > pos) { - ranges.add(new DocumentRange(pos, pos)) - } - } - } else { - def highlightFlags = parameters.get('highlightFlags') - def highlightPattern = parameters.get('highlightPattern') - - if (highlightFlags && highlightPattern) { - def regexp = createRegExp(highlightPattern) - - if (highlightFlags.indexOf('s') != -1) { - // Highlight strings - def pattern = Pattern.compile(regexp) - def matcher = pattern.matcher(textArea.text) - - while (matcher.find()) { - ranges.add(new DocumentRange(matcher.start(), matcher.end())) - } - } - - if ((highlightFlags.indexOf('t') != -1) && (highlightFlags.indexOf('r') != -1)) { - // Highlight type references - def pattern = Pattern.compile(regexp + '.*') - - for (def entry : hyperlinks.entrySet()) { - def hyperlink = entry.value - def name = getMostInnerTypeName(hyperlink.internalTypeName) - - if (pattern.matcher(name).matches()) { - ranges.add(new DocumentRange(hyperlink.startPosition, hyperlink.endPosition)) - } - } - } - } - } - } - - if (ranges) { - textArea.markAllHighlightColor = SELECT_HIGHLIGHT_COLOR - textArea.markAll(ranges) - setCaretPositionAndCenter(ranges.sort().get(0)) - } - } - - @CompileStatic - String getMostInnerTypeName(String typeName) { - int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1 - int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1 - int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex) - return typeName.substring(lastIndex) - } - - static class TypeHyperlinkData extends HyperlinkPage.HyperlinkData { - boolean enabled - String internalTypeName - - TypeHyperlinkData(int startPosition, int endPosition, String internalTypeName) { - super(startPosition, endPosition) - this.enabled = false - this.internalTypeName = internalTypeName - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/WebXmlFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/WebXmlFilePage.groovy deleted file mode 100644 index e4d14285..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/WebXmlFilePage.groovy +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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.xml.AbstractXmlPathFinder -import org.fife.ui.rsyntaxtextarea.SyntaxConstants - -import java.awt.Point - - -class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - WebXmlFilePage(API api, Container.Entry entry) { - this.api = api - this.entry = entry - // Load content file - def text = entry.inputStream.text - // Create hyperlinks - new PathFinder().find(text) - // Display - setText(text) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_XML } - - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - if (hyperlinkData instanceof TypeReferencePage.TypeHyperlinkData) { - def internalTypeName = hyperlinkData.internalTypeName - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, hyperlinkData.internalTypeName) - } else if (entries) { - api.openURI(x, y, entries, null, hyperlinkData.internalTypeName) - } - } else { - String path = hyperlinkData.path - def entry = searchEntry(this.entry.container.root, path) - if (entry) { - api.openURI(x, y, [entry], null, path) - } - } - } - } - - static Container.Entry searchEntry(Container.Entry parent, String path) { - if (path.charAt(0) == '/') - path = path.substring(1) - return recursiveSearchEntry(parent, path) - } - - static Container.Entry recursiveSearchEntry(Container.Entry parent, String path) { - def entry = parent.children.find { path.equals(it.path) } - - if (entry) { - return entry - } else { - entry = parent.children.find { path.startsWith(it.path + '/') } - return entry ? searchEntry(entry, path) : null - } - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def entry : hyperlinks.entrySet()) { - def data = entry.value - boolean enabled - - if (data instanceof TypeHyperlinkData) { - def internalTypeName = data.internalTypeName - enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - } else { - enabled = searchEntry(this.entry.container.root, data.path) != null - } - - if (data.enabled != enabled) { - data.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } - - static class PathHyperlinkData extends HyperlinkPage.HyperlinkData { - boolean enabled - String path - - PathHyperlinkData(int startPosition, int endPosition, String path) { - super(startPosition, endPosition) - this.enabled = false - this.path = path - } - } - - class PathFinder extends AbstractXmlPathFinder { - static HashSet typeHyperlinkPaths = [ - 'web-app/filter/filter-class', - 'web-app/listener/listener-class', - 'web-app/servlet/servlet-class'] - - static HashSet pathHyperlinkPaths = [ - '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'] - - PathFinder() { - super(typeHyperlinkPaths + pathHyperlinkPaths) - } - - void handle(String path, String text, int position) { - def trim = text.trim() - if (trim) { - int startIndex = position + text.indexOf(trim) - int endIndex = startIndex + trim.length() - - if (pathHyperlinkPaths.contains(path)) { - addHyperlink(new PathHyperlinkData(startIndex, endIndex, trim)) - } else { - def internalTypeName = trim.replace('.', '/') - addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) - } - } - } - } -} diff --git a/services/src/main/groovy/org/jd/gui/view/component/XmlFilePage.groovy b/services/src/main/groovy/org/jd/gui/view/component/XmlFilePage.groovy deleted file mode 100644 index 61015296..00000000 --- a/services/src/main/groovy/org/jd/gui/view/component/XmlFilePage.groovy +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.fife.ui.rsyntaxtextarea.SyntaxConstants - -import java.awt.* -import java.util.regex.Pattern - -class XmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { - protected API api - protected Container.Entry entry - protected Collection collectionOfIndexes - - XmlFilePage(API api, Container.Entry entry) { - this.api = api - this.entry = entry - // Load content file - def text = entry.inputStream.text - // Create hyperlinks - def pattern = Pattern.compile('(?s)<\\s*bean[^<]+class\\s*=\\s*"([^"]*)"') - def matcher = text =~ pattern - - while (matcher.find()) { - // Spring type reference found - def value = matcher.group(1) - def trim = value.trim() - - if (trim) { - int startIndex = matcher.start(1) - 1 - int endIndex = startIndex + value.length() + 2 - def internalTypeName = trim.replace('.', '/') - addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName)) - } - } - // Display - setText(text) - // Show hyperlinks - indexesChanged(api.collectionOfIndexes) - } - - String getSyntaxStyle() { SyntaxConstants.SYNTAX_STYLE_XML } - - protected boolean isHyperlinkEnabled(HyperlinkPage.HyperlinkData hyperlinkData) { hyperlinkData.enabled } - - protected void openHyperlink(int x, int y, HyperlinkPage.HyperlinkData hyperlinkData) { - if (hyperlinkData.enabled) { - // Save current position in history - def location = textArea.getLocationOnScreen() - int offset = textArea.viewToModel(new Point(x-location.x as int, y-location.y as int)) - def uri = entry.uri - api.addURI(new URI(uri.scheme, uri.authority, uri.path, 'position=' + offset, null)) - - // Open link - def internalTypeName = hyperlinkData.internalTypeName - def entries = collectionOfIndexes?.collect { it.getIndex('typeDeclarations')?.get(internalTypeName) }.flatten().grep { it!=null } - def rootUri = entry.container.root.uri.toString() - def sameContainerEntries = entries?.grep { it.uri.toString().startsWith(rootUri) } - - if (sameContainerEntries) { - api.openURI(x, y, sameContainerEntries, null, hyperlinkData.internalTypeName) - } else if (entries) { - api.openURI(x, y, entries, null, hyperlinkData.internalTypeName) - } - } - } - - // --- UriGettable --- // - URI getUri() { entry.uri } - - // --- ContentSavable --- // - String getFileName() { - def path = entry.path - int index = path.lastIndexOf('/') - return path.substring(index+1) - } - - // --- IndexesChangeListener --- // - void indexesChanged(Collection collectionOfIndexes) { - // Update the list of containers - this.collectionOfIndexes = collectionOfIndexes - // Refresh links - boolean refresh = false - - for (def entry : hyperlinks.entrySet()) { - def data = entry.value - def internalTypeName = data.internalTypeName - boolean enabled = collectionOfIndexes.find { it.getIndex('typeDeclarations')?.get(internalTypeName) } != null - - if (data.enabled != enabled) { - data.enabled = enabled - refresh = true - } - } - - if (refresh) { - textArea.repaint() - } - } -} diff --git a/services/src/main/java/org/fife/ui/rtextarea/Marker.java b/services/src/main/java/org/fife/ui/rtextarea/Marker.java new file mode 100644 index 00000000..0fd7fb83 --- /dev/null +++ b/services/src/main/java/org/fife/ui/rtextarea/Marker.java @@ -0,0 +1,29 @@ +/* + * 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(); + } +} diff --git a/services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java b/services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java new file mode 100644 index 00000000..cd56d519 --- /dev/null +++ b/services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java @@ -0,0 +1,32 @@ +/* + * 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()); + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/model/container/EarContainer.java b/services/src/main/java/org/jd/gui/model/container/EarContainer.java new file mode 100644 index 00000000..bff9332f --- /dev/null +++ b/services/src/main/java/org/jd/gui/model/container/EarContainer.java @@ -0,0 +1,21 @@ +/* + * 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"; } +} diff --git a/services/src/main/java/org/jd/gui/model/container/GenericContainer.java b/services/src/main/java/org/jd/gui/model/container/GenericContainer.java new file mode 100644 index 00000000..bc073e55 --- /dev/null +++ b/services/src/main/java/org/jd/gui/model/container/GenericContainer.java @@ -0,0 +1,179 @@ +/* + * 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 API api; + protected int rootNameCount; + protected Container.Entry root; + + public GenericContainer(API api, Container.Entry parentEntry, Path rootPath) { + this.api = api; + this.rootNameCount = rootPath.getNameCount(); + this.root = new Entry(parentEntry, rootPath, parentEntry.getUri()) { + public Entry newChildEntry(Path fsPath) { + return new Entry(parent, fsPath, null); + } + }; + } + + 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 = new URI(root.getUri().getScheme(), root.getUri().getHost(), root.getUri().getPath() + "!/" + getPath(), null); + } catch (URISyntaxException e) { + assert ExceptionUtil.printStackTrace(e); + } + } + return uri; + } + + public String getPath() { + if (strPath == null) { + if (rootNameCount == fsPath.getNameCount()) { + strPath = ""; + } else { + strPath = fsPath.subpath(rootNameCount, fsPath.getNameCount()).toString(); + if (strPath.endsWith(fsPath.getFileSystem().getSeparator())) { + // Cut last separator + strPath = strPath.substring(0, strPath.length()-fsPath.getFileSystem().getSeparator().length()); + } + } + } + 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 { + File tmpFile = File.createTempFile("jd-gui.", ".tmp." + fsPath.getFileName().toString()); + Path tmpPath = Paths.get(tmpFile.toURI()); + + Files.copy(fsPath, tmpPath); + + FileSystem subFileSystem = FileSystems.newFileSystem(tmpPath, null); + + if (subFileSystem != null) { + Iterator rootDirectories = subFileSystem.getRootDirectories().iterator(); + + if (rootDirectories.hasNext()) { + tmpFile.deleteOnExit(); + + 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(); + } + } +} diff --git a/services/src/main/java/org/jd/gui/model/container/JarContainer.java b/services/src/main/java/org/jd/gui/model/container/JarContainer.java new file mode 100644 index 00000000..e19a210e --- /dev/null +++ b/services/src/main/java/org/jd/gui/model/container/JarContainer.java @@ -0,0 +1,21 @@ +/* + * 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"; } +} diff --git a/services/src/main/java/org/jd/gui/model/container/WarContainer.java b/services/src/main/java/org/jd/gui/model/container/WarContainer.java new file mode 100644 index 00000000..2edae7a7 --- /dev/null +++ b/services/src/main/java/org/jd/gui/model/container/WarContainer.java @@ -0,0 +1,21 @@ +/* + * 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"; } +} diff --git a/services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java b/services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java new file mode 100644 index 00000000..82466311 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java @@ -0,0 +1,100 @@ +/* + * 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.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.io.File; +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) { + if (fragment != null) { + TypeFactory typeFactory = api.getTypeFactory(entry); + Type type = (typeFactory == null) ? null : typeFactory.make(api, entry, fragment); + + if (type != null) { + StringBuilder sb = new StringBuilder(type.getDisplayPackageName()); + int dashIndex = fragment.indexOf('-'); + + if (sb.length() > 0) { + sb.append('.'); + } + + sb.append(type.getDisplayTypeName()); + + 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; + } + } + + // Copy path of entry + String path = new File(entry.getUri()).getAbsolutePath(); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(path), null); + } + } +} diff --git a/app/src/main/groovy/org/jd/gui/view/bean/OpenTypeListCellBean.groovy b/services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java similarity index 51% rename from app/src/main/groovy/org/jd/gui/view/bean/OpenTypeListCellBean.groovy rename to services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java index d92eed4b..092c1602 100644 --- a/app/src/main/groovy/org/jd/gui/view/bean/OpenTypeListCellBean.groovy +++ b/services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java @@ -5,16 +5,8 @@ * copy and modify the code freely for non-commercial purposes. */ -package org.jd.gui.view.bean +package org.jd.gui.service.actions; -import org.jd.gui.api.model.Container - -import javax.swing.* - -class OpenTypeListCellBean { - String label - String packag - Icon icon - Collection entries - String typeName +public class InvalidFormatException extends RuntimeException{ + public InvalidFormatException(String message) { super(message); } } diff --git a/services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java b/services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java new file mode 100644 index 00000000..aee610c8 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java @@ -0,0 +1,41 @@ +/* + * 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 { + + public String getType() { return "ear"; } + + 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; + } + } + } + + public Container make(API api, Container.Entry parentEntry, Path rootPath) { + return new EarContainer(api, parentEntry, rootPath); + } +} diff --git a/services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java b/services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java new file mode 100644 index 00000000..45e3ecd4 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java @@ -0,0 +1,26 @@ +/* + * 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 { + + public String getType() { return "generic"; } + + public boolean accept(API api, Path rootPath) { return true; } + + public Container make(API api, Container.Entry parentEntry, Path rootPath) { + return new GenericContainer(api, parentEntry, rootPath); + } +} diff --git a/services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java b/services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java new file mode 100644 index 00000000..fb9eb2ae --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java @@ -0,0 +1,42 @@ +/* + * 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 { + + public String getType() { return "jar"; } + + 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; + } + } + } + + public Container make(API api, Container.Entry parentEntry, Path rootPath) { + return new JarContainer(api, parentEntry, rootPath); + } +} diff --git a/services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java b/services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java new file mode 100644 index 00000000..e57cceda --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java @@ -0,0 +1,41 @@ +/* + * 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 { + + public String getType() { return "war"; } + + 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; + } + } + } + + public Container make(API api, Container.Entry parentEntry, Path rootPath) { + return new WarContainer(api, parentEntry, rootPath); + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java new file mode 100644 index 00000000..43fdc096 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java @@ -0,0 +1,100 @@ +/* + * 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; + + api.addPanel(file.getName(), icon, "Location: " + file.getAbsolutePath(), mainPanel); + return mainPanel; + } + } + } + } + + return null; + } + + protected static class ContainerEntry implements Container.Entry { + protected static final Container PARENT_CONTAINER = new Container() { + public String getType() { return "generic"; } + 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); + } + } + + public Container getContainer() { return PARENT_CONTAINER; } + public Container.Entry getParent() { return null; } + public URI getUri() { return uri; } + public String getPath() { return path; } + public boolean isDirectory() { return file.isDirectory(); } + public long length() { return file.length(); } + public Collection getChildren() { return children; } + + 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; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java new file mode 100644 index 00000000..f6d0c6df --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java @@ -0,0 +1,69 @@ +/* + * 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; + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java new file mode 100644 index 00000000..3e7e395c --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java @@ -0,0 +1,37 @@ +/* + * 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.*; + +public class ClassFileLoaderProvider extends AbstractTypeFileLoaderProvider { + protected static final String[] EXTENSIONS = { "class" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Class files (*.class)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".class"); + } + + 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; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java new file mode 100644 index 00000000..74686141 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java @@ -0,0 +1,23 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Ear files (*.ear)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".ear"); + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java new file mode 100644 index 00000000..fa4f592c --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java @@ -0,0 +1,23 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Jar files (*.jar)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".jar"); + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java new file mode 100644 index 00000000..a83b0d4b --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java @@ -0,0 +1,42 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Java files (*.java)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".java"); + } + + 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()); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java new file mode 100644 index 00000000..8f3c759f --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java @@ -0,0 +1,30 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Log files (*.log)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".log"); + } + + 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; + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java new file mode 100644 index 00000000..e869543d --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java @@ -0,0 +1,23 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "War files (*.war)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".war"); + } +} diff --git a/services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java b/services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java new file mode 100644 index 00000000..4ee6b858 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java @@ -0,0 +1,59 @@ +/* + * 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" }; + + public String[] getExtensions() { return EXTENSIONS; } + public String getDescription() { return "Zip files (*.zip)"; } + + public boolean accept(API api, File file) { + return file.exists() && file.canRead() && file.getName().toLowerCase().endsWith(".zip"); + } + + 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; + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java index be18f498..0f6811ea 100644 --- a/services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java +++ b/services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java @@ -10,6 +10,7 @@ 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; @@ -17,14 +18,13 @@ 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 */ - AbstractIndexerProvider() { + public AbstractIndexerProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); @@ -32,7 +32,8 @@ public abstract class AbstractIndexerProvider implements Indexer { if (is != null) { properties.load(is); } - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } init(properties); @@ -40,18 +41,42 @@ public abstract class AbstractIndexerProvider implements Indexer { protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); - externalSelectors = (selectors == null) ? null : Arrays.asList(selectors.split(",")); + + if (selectors != null) { + externalSelectors = Arrays.asList(selectors.split(",")); + } String pathRegExp = properties.getProperty("pathRegExp"); - externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp); + + if (pathRegExp != null) { + externalPathPattern = Pattern.compile(pathRegExp); + } } - protected List getExternalSelectors() { return externalSelectors; } - protected Pattern getExternalPathPattern() { return externalPathPattern; } + 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; + } + } - public String[] getSelectors() { - return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); + 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; } @SuppressWarnings("unchecked") diff --git a/services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java index f5bb5c82..0a3f9b35 100644 --- a/services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java +++ b/services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java @@ -7,12 +7,13 @@ package org.jd.gui.service.indexer; -import groovyjarjarasm.asm.*; -import groovyjarjarasm.asm.signature.SignatureReader; -import groovyjarjarasm.asm.signature.SignatureVisitor; 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.*; @@ -38,26 +39,9 @@ public class ClassFileIndexerProvider extends AbstractIndexerProvider { fieldDeclarationSet, typeReferenceSet, superTypeNameSet, descriptorSet); protected SignatureIndexer signatureIndexer = new SignatureIndexer(typeReferenceSet); - /** - * @return local + optional external selectors - */ - public String[] getSelectors() { - List externalSelectors = getExternalSelectors(); - - if (externalSelectors == null) { - return new String[] { "*:file:*.class" }; - } else { - int size = externalSelectors.size(); - String[] selectors = new String[size+1]; - externalSelectors.toArray(selectors); - selectors[size] = "*:file:*.class"; - return selectors; - } - } + @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } - /** - * Index format : @see jd.gui.spi.Indexer - */ + @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { // Cleaning sets... @@ -156,8 +140,8 @@ public void index(API api, Container.Entry entry, Indexes indexes) { index.get(superTypeName).add(typeName); } } - - } catch (Exception ignore) { + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); } } @@ -180,7 +164,7 @@ public ClassIndexer( HashSet typeDeclarationSet, HashSet constructorDeclarationSet, HashSet methodDeclarationSet, HashSet fieldDeclarationSet, HashSet typeReferenceSet, HashSet superTypeNameSet, HashSet descriptorSet) { - super(Opcodes.ASM5); + super(Opcodes.ASM7); this.typeDeclarationSet = typeDeclarationSet; this.constructorDeclarationSet = constructorDeclarationSet; @@ -245,7 +229,7 @@ protected static class SignatureIndexer extends SignatureVisitor { protected HashSet typeReferenceSet; SignatureIndexer(HashSet typeReferenceSet) { - super(Opcodes.ASM5); + super(Opcodes.ASM7); this.typeReferenceSet = typeReferenceSet; } @@ -258,7 +242,7 @@ protected static class AnnotationIndexer extends AnnotationVisitor { protected HashSet descriptorSet; public AnnotationIndexer(HashSet descriptorSet) { - super(Opcodes.ASM5); + super(Opcodes.ASM7); this.descriptorSet = descriptorSet; } @@ -277,7 +261,7 @@ protected static class FieldIndexer extends FieldVisitor { protected AnnotationIndexer annotationIndexer; public FieldIndexer(HashSet descriptorSet, AnnotationIndexer annotationInexer) { - super(Opcodes.ASM5); + super(Opcodes.ASM7); this.descriptorSet = descriptorSet; this.annotationIndexer = annotationInexer; } @@ -298,7 +282,7 @@ protected static class MethodIndexer extends MethodVisitor { protected AnnotationIndexer annotationIndexer; public MethodIndexer(HashSet descriptorSet, AnnotationIndexer annotationIndexer) { - super(Opcodes.ASM5); + super(Opcodes.ASM7); this.descriptorSet = descriptorSet; this.annotationIndexer = annotationIndexer; } diff --git a/services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java new file mode 100644 index 00000000..02ae8b5a --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java @@ -0,0 +1,48 @@ +/* + * 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 org.jd.gui.util.exception.ExceptionUtil; + +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 e) { + assert ExceptionUtil.printStackTrace(e); + } + + 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); + } + } + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java new file mode 100644 index 00000000..973c3152 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java @@ -0,0 +1,75 @@ +/* + * 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java index a0371a6b..89ccb4e8 100644 --- a/services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java +++ b/services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java @@ -10,6 +10,7 @@ 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.antlr.v4.runtime.ANTLRInputStream; @@ -32,26 +33,9 @@ public class JavaFileIndexerProvider extends AbstractIndexerProvider { ANTLRJavaParser.parse(new ANTLRInputStream("class EarlyLoading{}"), new Listener(null)); } - /** - * @return local + optional external selectors - */ - public String[] getSelectors() { - List externalSelectors = getExternalSelectors(); - - if (externalSelectors == null) { - return new String[] { "*:file:*.java" }; - } else { - int size = externalSelectors.size(); - String[] selectors = new String[size+1]; - externalSelectors.toArray(selectors); - selectors[size] = "*:file:*.java"; - return selectors; - } - } + @Override public String[] getSelectors() { return appendSelectors("*:file:*.java"); } - /** - * Index format : @see jd.gui.spi.Indexer - */ + @Override @SuppressWarnings("unchecked") public void index(API api, Container.Entry entry, Indexes indexes) { try (InputStream inputStream = entry.getInputStream()) { @@ -79,7 +63,8 @@ public void index(API api, Container.Entry entry, Indexes indexes) { index.get(superTypeName).add(typeName); } } - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } } @@ -96,7 +81,7 @@ protected static class Listener extends AbstractJavaListener { protected HashSet stringSet = new HashSet<>(); protected HashMap> superTypeNamesMap = new HashMap<>(); - protected StringBuffer sbTypeDeclaration = new StringBuffer(); + protected StringBuilder sbTypeDeclaration = new StringBuilder(); public Listener(Container.Entry entry) { super(entry); diff --git a/services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java new file mode 100644 index 00000000..1fc151d9 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java @@ -0,0 +1,49 @@ +/* + * 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java new file mode 100644 index 00000000..1e5231b8 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java @@ -0,0 +1,29 @@ +/* + * 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); + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java new file mode 100644 index 00000000..314a3d0b --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java @@ -0,0 +1,51 @@ +/* + * 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java new file mode 100644 index 00000000..d277e415 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java @@ -0,0 +1,112 @@ +/* + * 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); + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java new file mode 100644 index 00000000..4eb5c3f5 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java @@ -0,0 +1,130 @@ +/* + * 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); + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java b/services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java new file mode 100644 index 00000000..0ab195bb --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java @@ -0,0 +1,33 @@ +/* + * 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"); } + + @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); + } + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java b/services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java new file mode 100644 index 00000000..a6aad62b --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java @@ -0,0 +1,27 @@ +/* + * 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)); + } +} diff --git a/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java b/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java new file mode 100644 index 00000000..4162a181 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java @@ -0,0 +1,81 @@ +/* + * 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 ESCAPE_UNICODE_CHARACTERS = "ClassFileSaverPreferences.escapeUnicodeCharacters"; + protected static final String OMIT_THIS_PREFIX = "ClassFileSaverPreferences.omitThisPrefix"; + protected static final String REALIGN_LINE_NUMBERS = "ClassFileSaverPreferences.realignLineNumbers"; + protected static final String WRITE_DEFAULT_CONSTRUCTOR = "ClassFileSaverPreferences.writeDefaultConstructor"; + protected static final String WRITE_LINE_NUMBERS = "ClassFileSaverPreferences.writeLineNumbers"; + protected static final String WRITE_METADATA = "ClassFileSaverPreferences.writeMetadata"; + + protected JCheckBox escapeUnicodeCharactersCheckBox; + protected JCheckBox omitThisPrefixCheckBox; + protected JCheckBox realignLineNumbersCheckBox; + protected JCheckBox writeDefaultConstructorCheckBox; + protected JCheckBox writeLineNumbersCheckBox; + protected JCheckBox writeMetadataCheckBox; + + public ClassFileSaverPreferencesProvider() { + super(new GridLayout(0,1)); + + escapeUnicodeCharactersCheckBox = new JCheckBox("Escape unicode characters"); + omitThisPrefixCheckBox = new JCheckBox("Omit the prefix 'this' if possible"); + realignLineNumbersCheckBox = new JCheckBox("Realign line numbers"); + writeDefaultConstructorCheckBox = new JCheckBox("Write default constructor"); + writeLineNumbersCheckBox = new JCheckBox("Write original line numbers"); + writeMetadataCheckBox = new JCheckBox("Write metadata"); + + add(escapeUnicodeCharactersCheckBox); + add(omitThisPrefixCheckBox); + add(realignLineNumbersCheckBox); + add(writeDefaultConstructorCheckBox); + 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) { + escapeUnicodeCharactersCheckBox.setSelected("true".equals(preferences.get(ESCAPE_UNICODE_CHARACTERS))); + omitThisPrefixCheckBox.setSelected("true".equals(preferences.get(OMIT_THIS_PREFIX))); + realignLineNumbersCheckBox.setSelected(!"false".equals(preferences.get(REALIGN_LINE_NUMBERS))); + writeDefaultConstructorCheckBox.setSelected("true".equals(preferences.get(WRITE_DEFAULT_CONSTRUCTOR))); + 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(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.isSelected())); + preferences.put(OMIT_THIS_PREFIX, Boolean.toString(omitThisPrefixCheckBox.isSelected())); + preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.isSelected())); + preferences.put(WRITE_DEFAULT_CONSTRUCTOR, Boolean.toString(writeDefaultConstructorCheckBox.isSelected())); + 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) {} +} diff --git a/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.java b/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.java new file mode 100644 index 00000000..ab7570d5 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileViewerPreferencesProvider.java @@ -0,0 +1,70 @@ +/* + * 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 ClassFileViewerPreferencesProvider extends JPanel implements PreferencesPanel { + protected static final String ESCAPE_UNICODE_CHARACTERS = "ClassFileViewerPreferences.escapeUnicodeCharacters"; + protected static final String OMIT_THIS_PREFIX = "ClassFileViewerPreferences.omitThisPrefix"; + protected static final String REALIGN_LINE_NUMBERS = "ClassFileViewerPreferences.realignLineNumbers"; + protected static final String DISPLAY_DEFAULT_CONSTRUCTOR = "ClassFileViewerPreferences.displayDefaultConstructor"; + + protected PreferencesPanel.PreferencesPanelChangeListener listener = null; + protected JCheckBox escapeUnicodeCharactersCheckBox; + protected JCheckBox omitThisPrefixCheckBox; + protected JCheckBox realignLineNumbersCheckBox; + protected JCheckBox displayDefaultConstructorCheckBox; + + public ClassFileViewerPreferencesProvider() { + super(new GridLayout(0,1)); + + escapeUnicodeCharactersCheckBox = new JCheckBox("Escape unicode characters"); + omitThisPrefixCheckBox = new JCheckBox("Omit the prefix 'this' if possible"); + realignLineNumbersCheckBox = new JCheckBox("Realign line numbers"); + displayDefaultConstructorCheckBox = new JCheckBox("Display default constructor"); + + add(escapeUnicodeCharactersCheckBox); + add(omitThisPrefixCheckBox); + add(realignLineNumbersCheckBox); + add(displayDefaultConstructorCheckBox); + } + + // --- PreferencesPanel --- // + @Override public String getPreferencesGroupTitle() { return "Viewer"; } + @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(!"false".equals(preferences.get(ESCAPE_UNICODE_CHARACTERS))); + omitThisPrefixCheckBox.setSelected("true".equals(preferences.get(OMIT_THIS_PREFIX))); + realignLineNumbersCheckBox.setSelected("true".equals(preferences.get(REALIGN_LINE_NUMBERS))); + displayDefaultConstructorCheckBox.setSelected("true".equals(preferences.get(DISPLAY_DEFAULT_CONSTRUCTOR))); + } + + @Override + public void savePreferences(Map preferences) { + preferences.put(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.isSelected())); + preferences.put(OMIT_THIS_PREFIX, Boolean.toString(omitThisPrefixCheckBox.isSelected())); + preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.isSelected())); + preferences.put(DISPLAY_DEFAULT_CONSTRUCTOR, Boolean.toString(displayDefaultConstructorCheckBox.isSelected())); + } + + @Override public boolean arePreferencesValid() { return true; } + + @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {} +} diff --git a/services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java b/services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java new file mode 100644 index 00000000..a7772a68 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java @@ -0,0 +1,91 @@ +/* + * 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java b/services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java new file mode 100644 index 00000000..932f2ecd --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java @@ -0,0 +1,110 @@ +/* + * 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java index b07b3288..8fd0d01d 100644 --- a/services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java @@ -8,6 +8,7 @@ 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; @@ -17,7 +18,6 @@ import java.util.regex.Pattern; public abstract class AbstractSourceSaverProvider implements SourceSaver { - protected List externalSelectors; protected Pattern externalPathPattern; @@ -32,7 +32,8 @@ public abstract class AbstractSourceSaverProvider implements SourceSaver { if (is != null) { properties.load(is); } - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } init(properties); @@ -40,17 +41,41 @@ public abstract class AbstractSourceSaverProvider implements SourceSaver { protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); - externalSelectors = (selectors == null) ? null : Arrays.asList(selectors.split(",")); + + if (selectors != null) { + externalSelectors = Arrays.asList(selectors.split(",")); + } String pathRegExp = properties.getProperty("pathRegExp"); - externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp); + + if (pathRegExp != null) { + externalPathPattern = Pattern.compile(pathRegExp); + } } - protected List getExternalSelectors() { return externalSelectors; } - protected Pattern getExternalPathPattern() { return externalPathPattern; } + 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; + } + } - public String[] getSelectors() { - return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); + 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; } } diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java index 721e0a9a..8d76f257 100644 --- a/services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java @@ -15,13 +15,13 @@ import org.jd.gui.util.decompiler.ContainerLoader; import org.jd.gui.util.decompiler.GuiPreferences; import org.jd.gui.util.decompiler.PlainTextPrinter; +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.List; import java.util.Map; public class ClassFileSourceSaverProvider extends AbstractSourceSaverProvider { @@ -39,23 +39,7 @@ public class ClassFileSourceSaverProvider extends AbstractSourceSaverProvider { protected PlainTextPrinter printer = new PlainTextPrinter(); protected ByteArrayOutputStream baos = new ByteArrayOutputStream(); - /** - * @return local + optional external selectors - */ - @Override - public String[] getSelectors() { - List externalSelectors = getExternalSelectors(); - - if (externalSelectors == null) { - return new String[] { "*:file:*.class" }; - } else { - int size = externalSelectors.size(); - String[] selectors = new String[size+1]; - externalSelectors.toArray(selectors); - selectors[size] = "*:file:*.class"; - return selectors; - } - } + @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } @Override public String getSourcePath(Container.Entry entry) { @@ -144,12 +128,16 @@ public void saveContent(API api, Controller controller, Listener listener, Path try (OutputStream os = new NewlineOutputStream(Files.newOutputStream(path))) { baos.writeTo(os); - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } - } catch (Exception ignore) { + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + try (BufferedWriter writer = Files.newBufferedWriter(path, Charset.defaultCharset())) { writer.write("// INTERNAL ERROR //"); - } catch (IOException ignoreAgain) { + } catch (IOException ee) { + assert ExceptionUtil.printStackTrace(ee); } } } diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java new file mode 100644 index 00000000..9d1143b2 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java @@ -0,0 +1,70 @@ +/* + * 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(); } +} diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java new file mode 100644 index 00000000..b3ab7229 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java @@ -0,0 +1,52 @@ +/* + * 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; + +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); + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java new file mode 100644 index 00000000..7ba66e0a --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java @@ -0,0 +1,23 @@ +/* + * 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()); + } +} diff --git a/services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java b/services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java new file mode 100644 index 00000000..6cc45727 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java @@ -0,0 +1,58 @@ +/* + * 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.*; +import java.util.HashMap; + +class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider { + + @Override public String[] getSelectors() { return appendSelectors("*:file:*.zip", "*:file:*.jar", "*:file:*.war", "*:file:*.ear"); } + + @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 tmpFile = File.createTempFile("jd-gui.", ".tmp.zip"); + tmpFile.delete(); + + 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(); + + Path tmpPath = Paths.get(tmpFile.getAbsolutePath()); + + Files.move(tmpPath, path); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java index fe0c7c38..2bb0be6a 100644 --- a/services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java +++ b/services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java @@ -8,6 +8,7 @@ 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; @@ -17,14 +18,13 @@ 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 */ - AbstractTreeNodeFactoryProvider() { + public AbstractTreeNodeFactoryProvider() { Properties properties = new Properties(); Class clazz = this.getClass(); @@ -32,7 +32,8 @@ public abstract class AbstractTreeNodeFactoryProvider implements TreeNodeFactory if (is != null) { properties.load(is); } - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } init(properties); @@ -40,17 +41,41 @@ public abstract class AbstractTreeNodeFactoryProvider implements TreeNodeFactory protected void init(Properties properties) { String selectors = properties.getProperty("selectors"); - externalSelectors = (selectors == null) ? null : Arrays.asList(selectors.split(",")); + + if (selectors != null) { + externalSelectors = Arrays.asList(selectors.split(",")); + } String pathRegExp = properties.getProperty("pathRegExp"); - externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp); + + if (pathRegExp != null) { + externalPathPattern = Pattern.compile(pathRegExp); + } } - protected List getExternalSelectors() { return externalSelectors; } - protected Pattern getExternalPathPattern() { return externalPathPattern; } + 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; + } + } - public String[] getSelectors() { - return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); + 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; } + + @Override public Pattern getPathPattern() { return externalPathPattern; } } diff --git a/services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..8173218c --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java @@ -0,0 +1,216 @@ +/* + * 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.Collections; +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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..a4fe40ad --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java @@ -0,0 +1,100 @@ +/* + * 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.decompiler.GuiPreferences; +import org.jd.gui.util.exception.ExceptionUtil; +import org.jd.gui.view.component.ClassFilePage; +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; + +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(ClassFilePage.class.getName()); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } + } + + @Override public String[] getSelectors() { return appendSelectors("*:file:*.class"); } + + @Override + @SuppressWarnings("unchecked") + public T make(API api, Container.Entry entry) { + int lastSlashIndex = entry.getPath().lastIndexOf('/'); + String name = entry.getPath().substring(lastSlashIndex+1); + return (T)new FileTreeNode(entry, new TreeNodeBean(name, 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 ClassFilePage(a, e); + } + + @Override + public String makeTip(API api, Container.Entry entry) { + File file = new File(entry.getContainer().getRoot().getUri()); + StringBuilder tip = new StringBuilder("Location: "); + + tip.append(file.getPath()); + 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..68a7a0c4 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java @@ -0,0 +1,47 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; + } + }; + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java new file mode 100644 index 00000000..532d81ea --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java @@ -0,0 +1,103 @@ +/* + * 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.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); + TreeNode node = new TreeNode(entry, new TreeNodeBean(label, 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(); } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..2cf75cc6 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..ab37c7a2 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java @@ -0,0 +1,34 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + T node = (T)new TreeNode(entry, "ear", new TreeNodeBean(name, ICON)); + // Add dummy node + node.add(new DefaultMutableTreeNode()); + return node; + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..59d9c845 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java @@ -0,0 +1,42 @@ +/* + * 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; + +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) { + return (T)new TreeNode(entry, new TreeNodeBean("ejb-jar.xml", "Location: " + entry.getUri().getPath(), 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..283950b1 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java @@ -0,0 +1,47 @@ +/* + * 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.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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, 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(); } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..288613d0 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..e79d2492 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java @@ -0,0 +1,76 @@ +/* + * 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.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 name = entry.getPath().substring(lastSlashIndex+1); + ImageIcon icon = isAEjbModule(entry) ? EJB_FILE_ICON : JAR_FILE_ICON; + T node = (T)new TreeNode(entry, "jar", new TreeNodeBean(name, 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, String containerType, Object userObject) { + super(entry, containerType, userObject); + } + + @Override + public Collection getChildren() { + return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren()); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..6c7b7af9 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java @@ -0,0 +1,55 @@ +/* + * 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 name = entry.getPath().substring(lastSlashIndex+1); + + return (T)new FileTreeNode(entry, new TreeNodeBean(name, 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) { + File file = new File(entry.getContainer().getRoot().getUri()); + StringBuilder tip = new StringBuilder("Location: "); + + tip.append(file.getPath()); + tip.append(""); + + return tip.toString(); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..08eac860 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java @@ -0,0 +1,44 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..2e97a48a --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..c9ec1acb --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..ea46973a --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java @@ -0,0 +1,42 @@ +/* + * 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; + +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) { + return (T)new TreeNode(entry, new TreeNodeBean("MANIFEST.MF", "Location: " + entry.getUri().getPath(), 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java new file mode 100644 index 00000000..9a4050d7 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java @@ -0,0 +1,29 @@ +/* + * 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:*", "war:dir:*", "ear:dir:*"); } + + @Override + public Pattern getPathPattern() { + if (externalPathPattern == null) { + return Pattern.compile("(WEB-INF|(WEB-INF\\/classes\\/)?META-IN(F|F\\/.*))"); + } else { + return externalPathPattern; + } + } + + @Override public ImageIcon getIcon() { return ICON; } + @Override public ImageIcon getOpenIcon() { return null; } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..e6d3a77e --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java @@ -0,0 +1,55 @@ +/* + * 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.OneTypeReferenceByLinePage; +import org.jd.gui.view.data.TreeNodeBean; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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 OneTypeReferenceByLinePage(api, entry); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java new file mode 100644 index 00000000..97790e92 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java @@ -0,0 +1,64 @@ +/* + * 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.util.Collection; + +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 + @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("/", "."); + T node = (T)new TreeNode(entry, new TreeNodeBean(label, 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()); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..c444b1c1 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java @@ -0,0 +1,47 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; + } + }; + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..f441aa43 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..21b0aab8 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java @@ -0,0 +1,80 @@ +/* + * 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.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"); } + + @Override + @SuppressWarnings("unchecked") + public T make(API api, Container.Entry entry) { + int lastSlashIndex = entry.getPath().lastIndexOf("/"); + String name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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); + } + } +} \ No newline at end of file diff --git a/services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..e6cee6e4 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java @@ -0,0 +1,34 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + T node = (T)new TreeNode(entry, "war", new TreeNodeBean(name, ICON)); + // Add dummy node + node.add(new DefaultMutableTreeNode()); + return node; + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java new file mode 100644 index 00000000..c32e1f51 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java @@ -0,0 +1,24 @@ +/* + * 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\\/.*"); + } else { + return externalPathPattern; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..5f7927c8 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java @@ -0,0 +1,42 @@ +/* + * 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; + +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) { + return (T)new TreeNode(entry, new TreeNodeBean("web.xml", "Location: " + entry.getUri().getPath(), 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.java new file mode 100644 index 00000000..ee635dad --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/WebinfClassesDirectoryTreeNodeFactoryProvider.java @@ -0,0 +1,18 @@ +/* + * 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 WebinfClassesDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider { + protected static final ImageIcon ICON = new ImageIcon(WebinfClassesDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource("org/jd/gui/images/packagefolder_obj.png")); + + @Override public String[] getSelectors() { return appendSelectors("war:dir:WEB-INF/classes"); } + @Override public ImageIcon getIcon() { return ICON; } + @Override public ImageIcon getOpenIcon() { return null; } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java new file mode 100644 index 00000000..752680cd --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java @@ -0,0 +1,18 @@ +/* + * 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; } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..4900e0db --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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; } + }; + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..6cc91cbd --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java @@ -0,0 +1,43 @@ +/* + * 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; + +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 name = entry.getPath().substring(lastSlashIndex+1); + return (T)new TreeNode(entry, new TreeNodeBean(name, "Location: " + entry.getUri().getPath(), 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java new file mode 100644 index 00000000..66e7afd4 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java @@ -0,0 +1,60 @@ +/* + * 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; + +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"); } + + @Override + @SuppressWarnings("unchecked") + public T make(API api, Container.Entry entry) { + int lastSlashIndex = entry.getPath().lastIndexOf("/"); + String name = entry.getPath().substring(lastSlashIndex+1); + T node = (T)new TreeNode(entry, "generic", new TreeNodeBean(name, ICON)); + // Add dummy node + node.add(new DefaultMutableTreeNode()); + return node; + } + + protected static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode { + protected String ct; + + public TreeNode(Container.Entry entry, String containerType, Object userObject) { + super(entry, userObject); + ct = containerType; + } + + // --- 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; + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java index 2b8876b7..a41dbde0 100644 --- a/services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java +++ b/services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java @@ -9,6 +9,7 @@ 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.*; @@ -20,7 +21,6 @@ import java.util.regex.Pattern; public abstract class AbstractTypeFactoryProvider implements TypeFactory { - protected List externalSelectors; protected Pattern externalPathPattern; @@ -35,7 +35,8 @@ public AbstractTypeFactoryProvider() { if (is != null) { properties.load(is); } - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); } init(properties); @@ -49,16 +50,34 @@ protected void init(Properties properties) { externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp); } - protected List getExternalSelectors() { return externalSelectors; } - protected Pattern getExternalPathPattern() { return externalPathPattern; } + 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; + } + } - public String[] getSelectors() { - return (externalSelectors==null) ? null : externalSelectors.toArray(new String[externalSelectors.size()]); + 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(StringBuffer sb, String descriptor, int length, int index, boolean varargsFlag) { + protected static int writeSignature(StringBuilder sb, String descriptor, int length, int index, boolean varargsFlag) { while (true) { // Print array : '[[?' ou '[L[?;' int dimensionLength = 0; @@ -170,7 +189,7 @@ protected static int writeSignature(StringBuffer sb, String descriptor, int len } protected static void writeMethodSignature( - StringBuffer sb, int typeAccess, int methodAccess, boolean isInnerClass, + StringBuilder sb, int typeAccess, int methodAccess, boolean isInnerClass, String constructorName, String methodName, String descriptor) { if (methodName.equals("")) { sb.append("{...}"); diff --git a/services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java index 42503cbe..eb27ac03 100644 --- a/services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java +++ b/services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java @@ -7,15 +7,11 @@ package org.jd.gui.service.type; -import groovyjarjarasm.asm.ClassReader; -import groovyjarjarasm.asm.Opcodes; -import groovyjarjarasm.asm.tree.ClassNode; -import groovyjarjarasm.asm.tree.FieldNode; -import groovyjarjarasm.asm.tree.InnerClassNode; -import groovyjarjarasm.asm.tree.MethodNode; 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; @@ -27,33 +23,24 @@ public class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider { static { // Early class loading - JavaType.class.getName(); + try { + Class.forName(JavaType.class.getName()); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } } // Create cache protected Cache cache = new Cache<>(); - /** - * @return local + optional external selectors - */ - public String[] getSelectors() { - List externalSelectors = getExternalSelectors(); - - if (externalSelectors == null) { - return new String[] { "*:file:*.class" }; - } else { - int size = externalSelectors.size(); - String[] selectors = new String[size+1]; - externalSelectors.toArray(selectors); - selectors[size] = "*:file:*.class"; - return selectors; - } - } + @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(); @@ -98,7 +85,8 @@ public Type make(API api, Container.Entry entry, String fragment) { try (InputStream is2 = entry.getInputStream()) { classReader = new ClassReader(is2); - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); return null; } break; @@ -118,7 +106,8 @@ public Type make(API api, Container.Entry entry, String fragment) { } type = new JavaType(entry, classReader, -1); - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); type = null; } @@ -128,8 +117,6 @@ public Type make(API api, Container.Entry entry, String fragment) { } static class JavaType implements Type { - - protected ClassNode classNode; protected Container.Entry entry; protected int access; protected String name; @@ -140,185 +127,195 @@ static class JavaType implements Type { protected String displayInnerTypeName; protected String displayPackageName; - protected List innerTypes = null; - protected List fields = null; - protected List methods = null; + protected List innerTypes; + protected List fields = new ArrayList<>(); + protected List methods = new ArrayList<>(); @SuppressWarnings("unchecked") - protected JavaType(Container.Entry entry, ClassReader classReader, int outerAccess) { - this.classNode = new ClassNode(); + protected JavaType(Container.Entry entry, ClassReader classReader, final int outerAccess) { this.entry = entry; - classReader.accept(classNode, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); - - this.access = (outerAccess == -1) ? classNode.access : outerAccess; - this.name = classNode.name; - - this.superName = ((access & Opcodes.ACC_INTERFACE) != 0) && "java/lang/Object".equals(classNode.superName) ? null : classNode.superName; - this.outerName = null; - this.displayInnerTypeName = null; + 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; + } - int indexDollar = name.lastIndexOf('$'); + @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 (indexDollar != -1) { - int indexSeparator = name.lastIndexOf('/'); - if (indexDollar > indexSeparator) { - for (final InnerClassNode innerClassNode : classNode.innerClasses) { - if (name.equals(innerClassNode.name)) { - // Inner class path found - if (innerClassNode.outerName != null) { - this.outerName = innerClassNode.outerName; - this.displayInnerTypeName = this.name.substring(this.outerName.length() + 1); + 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) { - this.displayPackageName = ""; - this.displayTypeName = (this.outerName == null) ? this.name : null; + displayPackageName = ""; + + if (outerName == null) { + displayTypeName = name; + } else { + displayTypeName = getDisplayTypeName(outerName, 0) + '.' + displayInnerTypeName; + } } else { - this.displayPackageName = this.name.substring(0, lastPackageSeparatorIndex).replace('/', '.'); - this.displayTypeName = (this.outerName == null) ? this.name.substring(lastPackageSeparatorIndex+1) : null; - } - } + displayPackageName = name.substring(0, lastPackageSeparatorIndex).replace('/', '.'); - public int getFlags() { return access; } - public String getName() { return name; } - public String getSuperName() { return superName; } - public String getOuterName() { return outerName; } - public String getDisplayPackageName() { return displayPackageName; } + if (outerName == null) { + displayTypeName = name; + } else { + displayTypeName = getDisplayTypeName(outerName, lastPackageSeparatorIndex) + '.' + displayInnerTypeName; + } - public String getDisplayTypeName() { - if (displayTypeName == null) { - displayTypeName = getDisplayTypeName(outerName, displayPackageName.length()) + '.' + displayInnerTypeName; + displayTypeName = displayTypeName.substring(lastPackageSeparatorIndex+1); } - return displayTypeName; - } - @SuppressWarnings("unchecked") - protected String getDisplayTypeName(String name, int packageLength) { - int indexDollar = name.lastIndexOf('$'); + 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); } - if (indexDollar > packageLength) { - Container.Entry outerEntry = getEntry(name); + public String getDisplayName() { + StringBuilder sb = new StringBuilder(); + sb.append(name).append(" : "); + writeSignature(sb, descriptor, descriptor.length(), 0, false); + return sb.toString(); + } + }); + } + return null; + } - if (outerEntry != null) { - try (InputStream is = outerEntry.getInputStream()) { - ClassReader classReader = new ClassReader(is); - ClassNode classNode = new ClassNode(); - classReader.accept(classNode, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + @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); } - for (final InnerClassNode innerClassNode : classNode.innerClasses) { - if (name.equals(innerClassNode.name)) { - // Inner class path found => Recursive call - return getDisplayTypeName(innerClassNode.outerName, packageLength) + '.' + innerClassNode.innerName; + 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(); } - } - } catch (IOException ignore) { + }); } + return null; } - } + }; - return packageLength > 0 ? name.substring(packageLength+1) : name; + classReader.accept(fieldsAndMethodsVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } - public String getDisplayInnerTypeName() { return displayInnerTypeName; } - public Icon getIcon() { return getTypeIcon(access); } - @SuppressWarnings("unchecked") - public List getInnerTypes() { - if (innerTypes == null) { - innerTypes = new ArrayList<>(classNode.innerClasses.size()); + protected String getDisplayTypeName(String name, int packageLength) { + int indexDollar = name.lastIndexOf('$'); - for (final InnerClassNode innerClassNode : classNode.innerClasses) { - if (((innerClassNode.access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_BRIDGE)) == 0) && this.name.equals(innerClassNode.outerName)) { - Container.Entry innerEntry = getEntry(innerClassNode.name); + if (indexDollar > packageLength) { + Container.Entry entry = getEntry(name); - if (innerEntry != null) { - try (InputStream is = innerEntry.getInputStream()) { - ClassReader classReader = new ClassReader(is); - innerTypes.add(new JavaType(innerEntry, classReader, innerClassNode.access)); - } catch (IOException ignore) { - } + 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 innerTypes; + + return name; } protected Container.Entry getEntry(String typeName) { String pathToFound = typeName + ".class"; - for (Container.Entry e : entry.getParent().getChildren()) { - if (e.getPath().equals(pathToFound)) { - return e; + for (Container.Entry entry : entry.getParent().getChildren()) { + if (entry.getPath().equals(pathToFound)) { + return entry; } } return null; } - @SuppressWarnings("unchecked") - public List getFields() { - if (fields == null) { - fields = new ArrayList<>(classNode.fields.size()); + @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; } + } - for (final FieldNode fieldNode : classNode.fields) { - if ((fieldNode.access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM)) == 0) { - fields.add(new Type.Field() { - public int getFlags() { return fieldNode.access; } - public String getName() { return fieldNode.name; } - public String getDescriptor() { return fieldNode.desc; } - public Icon getIcon() { return getFieldIcon(fieldNode.access); } + protected static class InnerClassVisitor extends ClassVisitor { + protected String name; + protected String outerName; + protected String innerName; - public String getDisplayName() { - StringBuffer sb = new StringBuffer(); - sb.append(fieldNode.name).append(" : "); - writeSignature(sb, fieldNode.desc, fieldNode.desc.length(), 0, false); - return sb.toString(); - } - }); - } - } - } - return fields; + public InnerClassVisitor(String name) { + super(Opcodes.ASM7); + this.name = name; } - @SuppressWarnings("unchecked") - public List getMethods() { - if (methods == null) { - methods = new ArrayList<>(classNode.methods.size()); - - for (final MethodNode methodNode : classNode.methods) { - if ((methodNode.access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM|Opcodes.ACC_BRIDGE)) == 0) { - methods.add(new Type.Method() { - public int getFlags() { return methodNode.access; } - public String getName() { return methodNode.name; } - public String getDescriptor() { return methodNode.desc; } - public Icon getIcon() { return getMethodIcon(methodNode.access); } - - public String getDisplayName() { - String constructorName = displayInnerTypeName; - boolean isInnerClass = (constructorName != null); - - if (constructorName == null) - constructorName = getDisplayTypeName(); - - StringBuffer sb = new StringBuffer(); - writeMethodSignature(sb, access, methodNode.access, isInnerClass, constructorName, methodNode.name, methodNode.desc); - return sb.toString(); - } - }); - } - } + @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; + } - return methods; + public String getInnerName() { + return innerName; } } } diff --git a/services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java b/services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java index 14603aa8..fc07babc 100644 --- a/services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java +++ b/services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java @@ -10,6 +10,7 @@ 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.*; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.ParserRuleContext; @@ -32,23 +33,9 @@ public class JavaFileTypeFactoryProvider extends AbstractTypeFactoryProvider { // Create cache protected Cache cache = new Cache<>(); - /** - * @return local + optional external selectors - */ - public String[] getSelectors() { - List externalSelectors = getExternalSelectors(); - - if (externalSelectors == null) { - return new String[] { "*:file:*.java" }; - } else { - int size = externalSelectors.size(); - String[] selectors = new String[size+1]; - externalSelectors.toArray(selectors); - selectors[size] = "*:file:*.java"; - return selectors; - } - } + @Override public String[] getSelectors() { return appendSelectors("*:file:*.java"); } + @Override public Collection make(API api, Container.Entry entry) { Listener listener = getListener(entry); @@ -59,6 +46,7 @@ public Collection make(API api, Container.Entry entry) { } } + @Override public Type make(API api, Container.Entry entry, String fragment) { Listener listener = getListener(entry); @@ -91,7 +79,8 @@ protected Listener getListener(Container.Entry entry) { try (InputStream inputStream = entry.getInputStream()) { ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener = new Listener(entry)); - } catch (IOException ignore) { + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); listener = null; } @@ -162,7 +151,7 @@ public JavaField(int access, String name, String descriptor) { public Icon getIcon() { return getFieldIcon(access); } public String getDisplayName() { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append(name).append(" : "); writeSignature(sb, descriptor, descriptor.length(), 0, false); return sb.toString(); @@ -194,7 +183,7 @@ public String getDisplayName() { if (constructorName == null) constructorName = type.getDisplayTypeName(); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); writeMethodSignature(sb, access, access, isInnerClass, constructorName, name, descriptor); return sb.toString(); } @@ -373,11 +362,11 @@ public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext } protected String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) { - StringBuffer paramDescriptors = null; + StringBuilder paramDescriptors = null; if (formalParameterList != null) { List formalParameters = formalParameterList.formalParameter(); - paramDescriptors = new StringBuffer("("); + paramDescriptors = new StringBuilder("("); for (JavaParser.FormalParameterContext formalParameter : formalParameters) { int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children); diff --git a/services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java b/services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java new file mode 100644 index 00000000..15bac3c3 --- /dev/null +++ b/services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java @@ -0,0 +1,30 @@ +/* + * 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); + } +} diff --git a/services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java b/services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java new file mode 100644 index 00000000..a2f89b05 --- /dev/null +++ b/services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java @@ -0,0 +1,107 @@ +/* + * 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.*; + +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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/util/decompiler/ClassFileSourcePrinter.java b/services/src/main/java/org/jd/gui/util/decompiler/ClassFileSourcePrinter.java index 3de4439f..a5559faf 100644 --- a/services/src/main/java/org/jd/gui/util/decompiler/ClassFileSourcePrinter.java +++ b/services/src/main/java/org/jd/gui/util/decompiler/ClassFileSourcePrinter.java @@ -9,8 +9,7 @@ import jd.core.printer.Printer; -public abstract class ClassFileSourcePrinter implements Printer -{ +public abstract class ClassFileSourcePrinter implements Printer { protected static final String TAB = " "; protected static final String NEWLINE = "\n"; diff --git a/services/src/main/java/org/jd/gui/util/decompiler/GuiPreferences.java b/services/src/main/java/org/jd/gui/util/decompiler/GuiPreferences.java index 6483f67b..c4dd9bfc 100644 --- a/services/src/main/java/org/jd/gui/util/decompiler/GuiPreferences.java +++ b/services/src/main/java/org/jd/gui/util/decompiler/GuiPreferences.java @@ -9,8 +9,7 @@ import jd.core.preferences.Preferences; -public class GuiPreferences extends Preferences -{ +public class GuiPreferences extends Preferences { protected boolean showPrefixThis; protected boolean unicodeEscape; protected boolean showLineNumbers; diff --git a/app/src/main/groovy/org/jd/gui/view/component/IconButton.groovy b/services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java similarity index 52% rename from app/src/main/groovy/org/jd/gui/view/component/IconButton.groovy rename to services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java index cb7e169f..d9316865 100644 --- a/app/src/main/groovy/org/jd/gui/view/component/IconButton.groovy +++ b/services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java @@ -5,17 +5,11 @@ * copy and modify the code freely for non-commercial purposes. */ -package org.jd.gui.view.component +package org.jd.gui.util.exception; -import java.awt.Insets - -import javax.swing.JButton - -class IconButton extends JButton { - - IconButton() { - focusPainted = false - borderPainted = false - margin = new Insets(0, 0, 0, 0) - } +public class ExceptionUtil { + public static boolean printStackTrace(Throwable throwable) { + throwable.printStackTrace(); + return true; + } } diff --git a/services/src/main/java/org/jd/gui/util/index/IndexUtil.java b/services/src/main/java/org/jd/gui/util/index/IndexUtil.java new file mode 100644 index 00000000..8e6298ce --- /dev/null +++ b/services/src/main/java/org/jd/gui/util/index/IndexUtil.java @@ -0,0 +1,43 @@ +/* + * 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 java.util.*; + +public class IndexUtil { + public static boolean containsInternalTypeName(Collection collectionOfIndexes, String internalTypeName) { + for (Indexes indexes : collectionOfIndexes) { + Map index = indexes.getIndex("typeDeclarations"); + if ((index != null) && (index.get(internalTypeName) != null)) { + return true; + } + } + + return false; + } + + @SuppressWarnings("unchecked") + public static List grepInternalTypeName(Collection collectionOfIndexes, String internalTypeName) { + ArrayList entries = new ArrayList<>(); + + for (Indexes indexes : collectionOfIndexes) { + Map index = indexes.getIndex("typeDeclarations"); + if (index != null) { + Collection collection = index.get(internalTypeName); + if (collection != null) { + entries.addAll(collection); + } + } + } + + return entries; + } +} diff --git a/services/src/main/java/org/jd/gui/util/io/TextReader.java b/services/src/main/java/org/jd/gui/util/io/TextReader.java new file mode 100644 index 00000000..6dc8e0c8 --- /dev/null +++ b/services/src/main/java/org/jd/gui/util/io/TextReader.java @@ -0,0 +1,41 @@ +/* + * 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 org.jd.gui.util.exception.ExceptionUtil; + +import java.io.*; + +public class TextReader { + + public static String getText(File file) { + try { + return getText(new FileInputStream(file)); + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); + return ""; + } + } + + public static String getText(InputStream is) { + StringBuilder sb = new StringBuilder(); + char[] charBuffer = new char[8192]; + int nbCharRead; + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { + while ((nbCharRead = reader.read(charBuffer)) != -1) { + // appends buffer + sb.append(charBuffer, 0, nbCharRead); + } + } catch (IOException e) { + assert ExceptionUtil.printStackTrace(e); + } + + return sb.toString(); + } +} diff --git a/services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java b/services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java index 2d7e4589..13bb4d5c 100644 --- a/services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java +++ b/services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java @@ -11,9 +11,9 @@ 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); @@ -28,9 +28,10 @@ public static void parse(CharStream input, JavaListener listener) { ParseTree tree = parser.compilationUnit(); ParseTreeWalker.DEFAULT.walk(listener, tree); - } catch (StackOverflowError ignore) { + } 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); } } } diff --git a/services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java b/services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java index b514c771..2b1ff0a0 100644 --- a/services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java +++ b/services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java @@ -11,16 +11,16 @@ 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.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 StringBuffer sb = new StringBuffer(); + protected StringBuilder sb = new StringBuilder(); protected HashMap typeNameCache = new HashMap<>(); public AbstractJavaListener(Container.Entry entry) { @@ -114,7 +114,8 @@ protected String resolveInternalTypeName(List identifiers) { typeNameCache.put(name, qualifiedName); return qualifiedName; } - } catch (ClassNotFoundException ignore) { + } catch (ClassNotFoundException e) { + assert ExceptionUtil.printStackTrace(e); } // Type not found diff --git a/services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java b/services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java index b02d0f04..45d8e408 100644 --- a/services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java +++ b/services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java @@ -7,6 +7,8 @@ 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; @@ -14,9 +16,8 @@ import java.util.*; public abstract class AbstractXmlPathFinder { - protected HashMap> tagNameToPaths = new HashMap<>(); - protected StringBuffer sb = new StringBuffer(200); + protected StringBuilder sb = new StringBuilder(200); public AbstractXmlPathFinder(Collection paths) { for (String path : paths) { @@ -80,7 +81,8 @@ public void find(String text) { break; } } - } catch (XMLStreamException ignore) { + } catch (XMLStreamException e) { + assert ExceptionUtil.printStackTrace(e); } } diff --git a/services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java b/services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java new file mode 100644 index 00000000..4f5ca99f --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java @@ -0,0 +1,427 @@ +/* + * 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.*; +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(new MouseWheelListener() { + public void mouseWheelMoved(MouseWheelEvent event) { + if ((event.getModifiers() & (Event.META_MASK|Event.CTRL_MASK)) != 0) { + int x = event.getX() + scrollPane.getX() - textArea.getX(); + int y = event.getY() + scrollPane.getY() - textArea.getY(); + int offset = textArea.viewToModel(new Point(x, y)); + + // Update font size + if (event.getWheelRotation() > 0) { + INCREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea); + } else { + DECREASE_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 e) { + assert ExceptionUtil.printStackTrace(e); + } + } else { + // Call default listeners + for (MouseWheelListener listener : mouseWheelListeners) { + listener.mouseWheelMoved(event); + } + } + } + }); + + 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 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 r = textArea.modelToView(start); + + if (r != null) { + // Visible + setCaretPositionAndCenter(start, end, r); + } else { + // Not visible yet + SwingUtilities.invokeLater(new Runnable() { + public void run() { + 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; + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/ClassFilePage.java b/services/src/main/java/org/jd/gui/view/component/ClassFilePage.java new file mode 100644 index 00000000..8bcf5ddd --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/ClassFilePage.java @@ -0,0 +1,293 @@ +/* + * 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 jd.core.Decompiler; +import jd.core.loader.Loader; +import jd.core.loader.LoaderException; +import jd.core.process.DecompilerImpl; +import org.fife.ui.rsyntaxtextarea.DocumentRange; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.jd.gui.api.API; +import org.jd.gui.api.model.Container; +import org.jd.gui.util.decompiler.ClassFileSourcePrinter; +import org.jd.gui.util.decompiler.ContainerLoader; +import org.jd.gui.util.decompiler.GuiPreferences; +import org.jd.gui.util.exception.ExceptionUtil; + +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultCaret; +import java.awt.*; +import java.io.DataInputStream; +import java.util.HashMap; +import java.util.Map; + +public class ClassFilePage extends TypePage { + protected static final String ESCAPE_UNICODE_CHARACTERS = "ClassFileViewerPreferences.escapeUnicodeCharacters"; + protected static final String OMIT_THIS_PREFIX = "ClassFileViewerPreferences.omitThisPrefix"; + protected static final String REALIGN_LINE_NUMBERS = "ClassFileViewerPreferences.realignLineNumbers"; + protected static final String DISPLAY_DEFAULT_CONSTRUCTOR = "ClassFileViewerPreferences.displayDefaultConstructor"; + + protected static final Decompiler DECOMPILER = new DecompilerImpl(); + + protected int maximumLineNumber = -1; + + static { + // Early class loading + String internalTypeName = ClassFilePage.class.getName().replace('.', '/'); + GuiPreferences preferences = new GuiPreferences(); + Loader loader = new Loader() { + public DataInputStream load(String internalTypePath) throws LoaderException { + return new DataInputStream(ClassFilePage.class.getClassLoader().getResourceAsStream(internalTypeName + ".class")); + } + public boolean canLoad(String internalTypePath) { return false; } + }; + ClassFileSourcePrinter printer = new ClassFileSourcePrinter() { + public boolean getRealignmentLineNumber() { return false; } + public boolean isShowPrefixThis() { return false; } + public boolean isUnicodeEscape() { return false; } + public void append(char c) {} + public void append(String s) {} + }; + try { + DECOMPILER.decompile(preferences, loader, printer, internalTypeName); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + } + } + + 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 + GuiPreferences p = new GuiPreferences(); + p.setUnicodeEscape(getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false)); + p.setShowPrefixThis(! getPreferenceValue(preferences, OMIT_THIS_PREFIX, false)); + p.setShowDefaultConstructor(getPreferenceValue(preferences, DISPLAY_DEFAULT_CONSTRUCTOR, false)); + p.setRealignmentLineNumber(getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false)); + + setShowMisalignment(p.getRealignmentLineNumber()); + // Init loader + ContainerLoader loader = new ContainerLoader(entry); + // Init printer + Printer printer = new Printer(p); + // Decompile class file + DECOMPILER.decompile(p, loader, printer, entry.getPath()); + + setText(printer.toString()); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } catch (Exception e) { + assert ExceptionUtil.printStackTrace(e); + setText("// INTERNAL ERROR //"); + } + + maximumLineNumber = getMaximumSourceLineNumber(); + } + + protected static boolean getPreferenceValue(Map preferences, String key, boolean defaultValue) { + String v = preferences.get(key); + + if (v == null) { + return defaultValue; + } else { + return Boolean.valueOf(v); + } + } + + public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; } + + // --- ContentSavable --- // + public String getFileName() { + String path = entry.getPath(); + int index = path.lastIndexOf('.'); + return path.substring(0, index) + ".java"; + } + + // --- LineNumberNavigable --- // + public int getMaximumLineNumber() { return maximumLineNumber; } + + 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); + } + } + } + + public boolean checkLineNumber(int lineNumber) { return lineNumber <= maximumLineNumber; } + + // --- PreferencesChangeListener --- // + 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 Printer extends ClassFileSourcePrinter { + protected StringBuilder stringBuffer = new StringBuilder(10*1024); + protected boolean realignmentLineNumber; + protected boolean showPrefixThis; + protected boolean unicodeEscape; + protected HashMap referencesCache = new HashMap<>(); + + public Printer(GuiPreferences preferences) { + this.realignmentLineNumber = preferences.getRealignmentLineNumber(); + this.showPrefixThis = preferences.isShowPrefixThis(); + this.unicodeEscape = preferences.isUnicodeEscape(); + } + + public boolean getRealignmentLineNumber() { return realignmentLineNumber; } + public boolean isShowPrefixThis() { return showPrefixThis; } + public boolean isUnicodeEscape() { return unicodeEscape; } + + public void append(char c) { stringBuffer.append(c); } + public void append(String s) { stringBuffer.append(s); } + + // Manage line number and misalignment + int textAreaLineNumber = 1; + + public void start(int maxLineNumber, int majorVersion, int minorVersion) { + super.start(maxLineNumber, majorVersion, minorVersion); + + if (maxLineNumber == 0) { + scrollPane.setLineNumbersEnabled(false); + } else { + setMaxLineNumber(maxLineNumber); + } + } + public void startOfLine(int sourceLineNumber) { + super.startOfLine(sourceLineNumber); + setLineNumber(textAreaLineNumber, sourceLineNumber); + } + public void endOfLine() { + super.endOfLine(); + textAreaLineNumber++; + } + public void extraLine(int count) { + super.extraLine(count); + if (realignmentLineNumber) { + textAreaLineNumber += count; + } + } + + // --- Add strings --- // + public void printString(String s, String scopeInternalName) { + strings.add(new TypePage.StringData(stringBuffer.length(), s.length(), s, scopeInternalName)); + super.printString(s, scopeInternalName); + } + + // --- Add references --- // + public void printTypeImport(String internalName, String name) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, null))); + super.printTypeImport(internalName, name); + } + + public void printType(String internalName, String name, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, null, null, scopeInternalName))); + super.printType(internalName, name, scopeInternalName); + } + + public void printField(String internalName, String name, String descriptor, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))); + super.printField(internalName, name, descriptor, scopeInternalName); + } + public void printStaticField(String internalName, String name, String descriptor, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))); + super.printStaticField(internalName, name, descriptor, scopeInternalName); + } + + public void printConstructor(String internalName, String name, String descriptor, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, "", descriptor, scopeInternalName))); + super.printConstructor(internalName, name, descriptor, scopeInternalName); + } + + public void printMethod(String internalName, String name, String descriptor, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))); + super.printMethod(internalName, name, descriptor, scopeInternalName); + } + public void printStaticMethod(String internalName, String name, String descriptor, String scopeInternalName) { + addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalName, name, descriptor, scopeInternalName))); + super.printStaticMethod(internalName, name, descriptor, scopeInternalName); + } + + 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; + } + + // --- Add declarations --- // + public void printTypeDeclaration(String internalName, String name) { + TypePage.DeclarationData data = new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, null, null); + declarations.put(internalName, data); + typeDeclarations.put(stringBuffer.length(), data); + super.printTypeDeclaration(internalName, name); + } + + public void printFieldDeclaration(String internalName, String name, String descriptor) { + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)); + super.printFieldDeclaration(internalName, name, descriptor); + } + public void printStaticFieldDeclaration(String internalName, String name, String descriptor) { + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)); + super.printStaticFieldDeclaration(internalName, name, descriptor); + } + + public void printConstructorDeclaration(String internalName, String name, String descriptor) { + declarations.put(internalName + "--" + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, "", descriptor)); + super.printConstructorDeclaration(internalName, name, descriptor); + } + public void printStaticConstructorDeclaration(String internalName, String name) { + declarations.put(internalName + "--()V", new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, "", "()V")); + super.printStaticConstructorDeclaration(internalName, name); + } + + public void printMethodDeclaration(String internalName, String name, String descriptor) { + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)); + super.printMethodDeclaration(internalName, name, descriptor); + } + public void printStaticMethodDeclaration(String internalName, String name, String descriptor) { + declarations.put(internalName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalName, name, descriptor)); + super.printStaticMethodDeclaration(internalName, name, descriptor); + } + + public String toString() { return stringBuffer.toString(); } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java b/services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java new file mode 100644 index 00000000..e73c9583 --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java @@ -0,0 +1,315 @@ +/* + * 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.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; + + public void setMaxLineNumber(int maxLineNumber) { + if (maxLineNumber > 0) { + if (lineNumberMap == null) { + lineNumberMap = new int[maxLineNumber * 3 / 2]; + } else if (lineNumberMap.length <= maxLineNumber) { + int[] tmp = new int[maxLineNumber * 3 / 2]; + System.arraycopy(lineNumberMap, 0, tmp, 0, lineNumberMap.length); + lineNumberMap = tmp; + } + + if (this.maxLineNumber < maxLineNumber) { + this.maxLineNumber = maxLineNumber; + } + } + } + + public void setLineNumber(int textAreaLineNumber, int originalLineNumber) { + if (originalLineNumber > 0) { + setMaxLineNumber(textAreaLineNumber); + lineNumberMap[textAreaLineNumber] = originalLineNumber; + } + } + + public void clearLineNumbers() { + if (lineNumberMap != null) { + Arrays.fill(lineNumberMap, 0); + } + } + + public int getMaximumSourceLineNumber() { return maxLineNumber; } + + public 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 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 data = entry.getValue(); + if ((data != null) && (t.getOffset() < data.endPosition) && (t.getOffset() >= data.startPosition) && isHyperlinkEnabled(data)) { + return true; + } + } + return super.getUnderlineForToken(t); + } + + @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; + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java b/services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java new file mode 100644 index 00000000..5da3c571 --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java @@ -0,0 +1,161 @@ +/* + * 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.IndexUtil; +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; + +public class EjbJarXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { + protected API api; + protected Container.Entry entry; + protected Collection collectionOfIndexes; + + 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); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } + + 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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // Refresh links + boolean refresh = false; + + for (Map.Entry entry : hyperlinks.entrySet()) { + TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue(); + String internalTypeName = entryData.internalTypeName; + boolean enabled = IndexUtil.containsInternalTypeName(collectionOfIndexes, 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)); + } + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java b/services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java new file mode 100644 index 00000000..f2dfef79 --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java @@ -0,0 +1,115 @@ +/* + * 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 { + 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/JavaFilePage.java b/services/src/main/java/org/jd/gui/view/component/JavaFilePage.java new file mode 100644 index 00000000..ab4e3b8e --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/JavaFilePage.java @@ -0,0 +1,747 @@ +/* + * 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); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } + + 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 { + 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; + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/LogPage.java b/services/src/main/java/org/jd/gui/view/component/LogPage.java new file mode 100644 index 00000000..c35c742b --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/LogPage.java @@ -0,0 +1,144 @@ +/* + * 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.IndexUtil; + +import java.awt.*; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class LogPage extends HyperlinkPage implements UriGettable, IndexesChangeListener { + protected API api; + protected URI uri; + protected Collection collectionOfIndexes; + + 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); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } + + 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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // 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 = IndexUtil.containsInternalTypeName(collectionOfIndexes, 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); + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java b/services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java new file mode 100644 index 00000000..2d8243ec --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java @@ -0,0 +1,214 @@ +/* + * 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.IndexUtil; +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; + +public class ManifestFilePage extends HyperlinkPage implements UriGettable, IndexesChangeListener { + protected API api; + protected Container.Entry entry; + protected Collection collectionOfIndexes; + + 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); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } + + 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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // 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 = IndexUtil.containsInternalTypeName(collectionOfIndexes, 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; + } + } +} + diff --git a/services/src/main/java/org/jd/gui/view/component/OneTypeReferenceByLinePage.java b/services/src/main/java/org/jd/gui/view/component/OneTypeReferenceByLinePage.java new file mode 100644 index 00000000..0514bec6 --- /dev/null +++ b/services/src/main/java/org/jd/gui/view/component/OneTypeReferenceByLinePage.java @@ -0,0 +1,136 @@ +/* + * 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.IndexUtil; + +import java.awt.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class OneTypeReferenceByLinePage extends TypeReferencePage implements UriGettable, IndexesChangeListener { + protected API api; + protected Container.Entry entry; + protected Collection collectionOfIndexes; + + public OneTypeReferenceByLinePage(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()); + // Show hyperlinks + indexesChanged(api.getCollectionOfIndexes()); + } + + 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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) { + // Update the list of containers + this.collectionOfIndexes = collectionOfIndexes; + // Refresh links + boolean refresh = false; + + for (Map.Entry entry : hyperlinks.entrySet()) { + TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue(); + String internalTypeName = entryData.internalTypeName; + boolean enabled = IndexUtil.containsInternalTypeName(collectionOfIndexes, internalTypeName); + + if (entryData.enabled != enabled) { + entryData.enabled = enabled; + refresh = true; + } + } + + if (refresh) { + textArea.repaint(); + } + } +} diff --git a/services/src/main/java/org/jd/gui/view/component/ErrorStrip.java b/services/src/main/java/org/jd/gui/view/component/RoundMarkErrorStrip.java similarity index 96% rename from services/src/main/java/org/jd/gui/view/component/ErrorStrip.java rename to services/src/main/java/org/jd/gui/view/component/RoundMarkErrorStrip.java index 70e9435d..30503062 100644 --- a/services/src/main/java/org/jd/gui/view/component/ErrorStrip.java +++ b/services/src/main/java/org/jd/gui/view/component/RoundMarkErrorStrip.java @@ -9,10 +9,32 @@ 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 look... + * '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 * @@ -23,37 +45,8 @@ * RSyntaxTextArea.License.txt file for details. */ -import java.awt.Color; -import java.awt.Component; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Rectangle; -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.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; -import javax.swing.JComponent; -import javax.swing.ToolTipManager; -import javax.swing.UIManager; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.text.BadLocationException; - -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; - - /** - * A component to sit alongside an {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea} that displays + * A component to sit alongside an {@link RSyntaxTextArea} that displays * colored markers for locations of interest (parser errors, marked * occurrences, etc.).

* @@ -62,9 +55,9 @@ * 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(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)} + * {@link #setLevelThreshold(ParserNotice.Level)} * to be displayed in this error strip. The default threshold is - * {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.

+ * {@link ParserNotice.Level#WARNING}.

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

@@ -87,7 +80,7 @@
  *       the notices being reloaded from the Markers (removing any Markers that
  *       are now "empty").
  */
-public class ErrorStrip extends JComponent {
+public class RoundMarkErrorStrip extends JComponent {
 
     /**
      * The text area.
@@ -164,7 +157,7 @@ public class ErrorStrip extends JComponent {
      *
      * @param textArea The text area we are examining.
      */
-    public ErrorStrip(RSyntaxTextArea textArea) {
+    public RoundMarkErrorStrip(RSyntaxTextArea textArea) {
         this.textArea = textArea;
         listener = new Listener();
         ToolTipManager.sharedInstance().registerComponent(this);
@@ -299,7 +292,7 @@ public Dimension getPreferredSize() {
      * defined in the ParserNotice class.
      *
      * @return The minimum severity.
-     * @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
+     * @see #setLevelThreshold(ParserNotice.Level)
      */
     public ParserNotice.Level getLevelThreshold() {
         return levelThreshold;
@@ -530,7 +523,7 @@ public void setFollowCaret(boolean follow) {
      * 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 org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
+     * {@link ParserNotice.Level#WARNING}.
      *
      * @param level The new severity threshold.
      * @see #getLevelThreshold()
@@ -723,8 +716,8 @@ public int getLength() {
             return range.getEndOffset() - range.getStartOffset();
         }
 
-        public ParserNotice.Level getLevel() {
-            return ParserNotice.Level.INFO; // Won't matter
+        public Level getLevel() {
+            return Level.INFO; // Won't matter
         }
 
         public int getLine() {
diff --git a/services/src/main/java/org/jd/gui/view/component/TextPage.java b/services/src/main/java/org/jd/gui/view/component/TextPage.java
new file mode 100644
index 00000000..bc05b7d3
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/component/TextPage.java
@@ -0,0 +1,46 @@
+/*
+ * 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.*;
+
+public class TextPage extends AbstractTextPage implements ContentCopyable, ContentSelectable, ContentSavable {
+
+    // --- ContentCopyable --- //
+    public void copy() {
+        if (textArea.getSelectionStart() == textArea.getSelectionEnd()) {
+            getToolkit().getSystemClipboard().setContents(new StringSelection(""), null);
+        } else {
+            textArea.copyAsRtf();
+        }
+    }
+
+    // --- ContentSelectable --- //
+    public void selectAll() {
+        textArea.selectAll();
+    }
+
+    // --- ContentSavable --- //
+    public String getFileName() { return "file.txt"; }
+
+    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);
+        }
+    }
+}
diff --git a/services/src/main/java/org/jd/gui/view/component/TypePage.java b/services/src/main/java/org/jd/gui/view/component/TypePage.java
new file mode 100644
index 00000000..a426a8b0
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/component/TypePage.java
@@ -0,0 +1,526 @@
+/*
+ * 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.IndexUtil;
+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.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 collectionOfIndexes;
+
+    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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) {
+        // Update the list of containers
+        this.collectionOfIndexes = collectionOfIndexes;
+        // Refresh links
+        boolean refresh = false;
+
+        for (ReferenceData reference : references) {
+            String typeName = reference.typeName;
+            boolean enabled;
+
+            if (reference.name != null) {
+                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;
+                }
+            } else {
+                enabled = false;
+                for (Indexes indexes : collectionOfIndexes) {
+                    Map index = indexes.getIndex("typeDeclarations");
+                    if ((index != null) && (index.get(typeName) != null)) {
+                        enabled = true;
+                        break;
+                    }
+                }
+            }
+
+            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<>();
+
+        for (Indexes indexes : collectionOfIndexes) {
+            Map index = indexes.getIndex("typeDeclarations");
+            if (index != null) {
+                Collection collection = index.get(typeName);
+                if (collection != null) {
+                    entries.addAll(collection);
+                }
+            }
+        }
+
+        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;
+        }
+    }
+
+    public 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;
+
+        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); }
+    }
+}
diff --git a/services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java b/services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java
new file mode 100644
index 00000000..b8f4c169
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java
@@ -0,0 +1,116 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java b/services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java
new file mode 100644
index 00000000..c6924001
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java
@@ -0,0 +1,213 @@
+/*
+ * 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.IndexUtil;
+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;
+
+public class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {
+    protected API api;
+    protected Container.Entry entry;
+    protected Collection collectionOfIndexes;
+
+    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);
+        // Show hyperlinks
+        indexesChanged(api.getCollectionOfIndexes());
+    }
+
+    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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) {
+        // Update the list of containers
+        this.collectionOfIndexes = collectionOfIndexes;
+        // 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 = IndexUtil.containsInternalTypeName(collectionOfIndexes, 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));
+                }
+            }
+        }
+    }
+}
diff --git a/services/src/main/java/org/jd/gui/view/component/XmlFilePage.java b/services/src/main/java/org/jd/gui/view/component/XmlFilePage.java
new file mode 100644
index 00000000..05dac5bc
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/component/XmlFilePage.java
@@ -0,0 +1,132 @@
+/*
+ * 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.IndexUtil;
+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.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class XmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {
+    protected API api;
+    protected Container.Entry entry;
+    protected Collection collectionOfIndexes;
+
+    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);
+        // Show hyperlinks
+        indexesChanged(api.getCollectionOfIndexes());
+    }
+
+    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 = IndexUtil.grepInternalTypeName(collectionOfIndexes, 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 collectionOfIndexes) {
+        // Update the list of containers
+        this.collectionOfIndexes = collectionOfIndexes;
+        // Refresh links
+        boolean refresh = false;
+
+        for (Map.Entry entry : hyperlinks.entrySet()) {
+            TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue();
+            String internalTypeName = data.internalTypeName;
+            boolean enabled = IndexUtil.containsInternalTypeName(collectionOfIndexes, internalTypeName);
+
+            if (data.enabled != enabled) {
+                data.enabled = enabled;
+                refresh = true;
+            }
+        }
+
+        if (refresh) {
+            textArea.repaint();
+        }
+    }
+}
diff --git a/services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java b/services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java
new file mode 100644
index 00000000..4523df69
--- /dev/null
+++ b/services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+    }
+}