diff --git a/Ghidra/Features/Base/developer_scripts/DumpGhidraCapabilitiesScript.java b/Ghidra/Features/Base/developer_scripts/DumpGhidraCapabilitiesScript.java deleted file mode 100644 index 5d18fdccf8..0000000000 --- a/Ghidra/Features/Base/developer_scripts/DumpGhidraCapabilitiesScript.java +++ /dev/null @@ -1,111 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.util.*; - -import generic.jar.ResourceFile; -import ghidra.app.script.GhidraScript; -import ghidra.app.services.Analyzer; -import ghidra.framework.Application; -import ghidra.framework.plugintool.PluginConfigurationModel; -import ghidra.framework.plugintool.util.PluginDescription; -import ghidra.util.classfinder.ClassSearcher; - -public class DumpGhidraCapabilitiesScript extends GhidraScript { - - private Map> pluginMap = new HashMap<>(); - private Map> analyzerMap = new HashMap<>(); - - @Override - public void run() throws Exception { - PluginConfigurationModel model = new PluginConfigurationModel(state.getTool()); - List descriptions = model.getAllPluginDescriptions(); - for (PluginDescription pluginDescription : descriptions) { - String moduleName = pluginDescription.getModuleName(); - - if (moduleName == null) { - moduleName = "[ No Module ]"; - } - - addPlugin(moduleName, pluginDescription); - } - - List instances = ClassSearcher.getInstances(Analyzer.class); - for (Analyzer analyzer : instances) { - Class clazz = analyzer.getClass(); - - ResourceFile module = Application.getModuleContainingClass(clazz.getName()); - - String moduleName; - if (module == null) { - moduleName = "[ No Module ]"; - } - else { - moduleName = module.getName(); - } - - addAnalyzer(moduleName, analyzer); - } - - Set set = new HashSet<>(pluginMap.keySet()); - set.addAll(analyzerMap.keySet()); - List list = new ArrayList<>(set); - Collections.sort(list); - System.out.println("Modules:"); - for (String module : list) { - System.out.println("\t" + module); - List plugins = pluginMap.get(module); - if (plugins != null && !plugins.isEmpty()) { - System.out.println("\t\tPlugins: "); - Collections.sort(plugins); - for (PluginDescription pd : plugins) { - System.out.println("\t\t\t" + pd.getName()); - System.out.println("\t\t\t\t" + pd.getShortDescription()); - System.out.println("\t\t\t\t" + pd.getDescription()); - } - } - - List analyzers = analyzerMap.get(module); - if (analyzers != null && !analyzers.isEmpty()) { - System.out.println("\t\tAnalyzers: "); - Collections.sort(analyzers, - (arg0, arg1) -> arg0.getName().compareTo(arg1.getName())); - for (Analyzer analyzer : analyzers) { - System.out.println("\t\t\t" + analyzer.getName()); - System.out.println("\t\t\t\t" + analyzer.getDescription()); - } - } - } - } - - private void addAnalyzer(String moduleName, Analyzer analyzer) { - List list = analyzerMap.get(moduleName); - if (list == null) { - list = new ArrayList<>(); - analyzerMap.put(moduleName, list); - } - list.add(analyzer); - - } - - private void addPlugin(String moduleName, PluginDescription pluginDescription) { - List list = pluginMap.get(moduleName); - if (list == null) { - list = new ArrayList<>(); - pluginMap.put(moduleName, list); - } - list.add(pluginDescription); - } -} diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Tool/Configure_Tool.htm b/Ghidra/Features/Base/src/main/help/help/topics/Tool/Configure_Tool.htm index a4b8d92fcd..884249c031 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Tool/Configure_Tool.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Tool/Configure_Tool.htm @@ -28,8 +28,12 @@

The Configure Tool dialog shows a list of plugin packages that can be added to - the tool. Clicking the checkbox will add (or remove) all the plugins in the package to the - tool. Clicking on the Configure link will bring up a dialog for adding individual + the tool. Clicking the unchecked checkbox will add all the plugins in the package that are + at the supported level (typically the RELEASED level) for that package. Any plugins in the + package not at the supported level or higher will not be automatically added. Clicking + the checked checkbox will remove all plugins from the tool that belong to that package. + +

Clicking on the Configure link will bring up a dialog for adding individual plugins.

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/DeveloperPluginPackage.java b/Ghidra/Features/Base/src/main/java/ghidra/app/DeveloperPluginPackage.java index e4076832a1..4e989ff388 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/DeveloperPluginPackage.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/DeveloperPluginPackage.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +16,20 @@ package ghidra.app; import ghidra.framework.plugintool.util.PluginPackage; +import ghidra.framework.plugintool.util.PluginStatus; import resources.ResourceManager; public class DeveloperPluginPackage extends PluginPackage { public static final String NAME = "Developer"; - + public DeveloperPluginPackage() { super(NAME, ResourceManager.loadImage("images/applications-engineering.png"), - "These plugins provide features useful for developing and debugging plugins.", - DEVELOPER_PRIORITY); + "These plugins provide features useful for developing and debugging plugins.", + DEVELOPER_PRIORITY); + } + + @Override + public PluginStatus getActivationLevel() { + return PluginStatus.STABLE; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/GraphPluginPackage.java b/Ghidra/Features/Base/src/main/java/ghidra/app/GraphPluginPackage.java deleted file mode 100644 index 4608fb65f5..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/GraphPluginPackage.java +++ /dev/null @@ -1,29 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app; - -import ghidra.framework.plugintool.util.PluginPackage; -import resources.ResourceManager; - -public class GraphPluginPackage extends PluginPackage { - - public static final String NAME = "Graph"; - - public GraphPluginPackage() { - super(NAME, ResourceManager.loadImage("images/katomic.png"), - "Provides plugins that display information in graph form.", FEATURE_PRIORITY); - } -} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java index e22c3aad16..fa08427e86 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManageFrontEndToolTest.java @@ -32,28 +32,15 @@ import ghidra.test.TestEnv; import ghidra.util.classfinder.ClassSearcher; /** - * - * Test configure the Front End tool. - * - * + * Test configure the Front End tool */ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest { private TestEnv env; private FrontEndTool tool; private FrontEndPlugin plugin; - private ManagePluginsDialog provider; - + private ManagePluginsDialog managePluginsDialog; private PluginConfigurationModel pluginModel; - private PluginManagerComponent pluginManagerComponent; - - /** - * Constructor for ManageFrontEndToolTest. - * @param arg0 - */ - public ManageFrontEndToolTest() { - super(); - } @Before public void setUp() throws Exception { @@ -65,15 +52,12 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest showProvider(); } - /* - * @see TestCase#tearDown() - */ @After public void tearDown() throws Exception { runSwing(() -> { tool.setConfigChanged(false); - provider.close(); + managePluginsDialog.close(); }); env.dispose(); } @@ -97,7 +81,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest final Plugin p = getPlugin(tool, ArchivePlugin.class); assertNotNull(p); runSwing(() -> { - provider.close(); + managePluginsDialog.close(); tool.removePlugins(new Plugin[] { p }); }); @@ -108,7 +92,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest action = getAction(tool, plugin.getName(), "Close Project"); performAction(action, true); - assertTrue(!provider.isVisible()); + assertFalse(managePluginsDialog.isVisible()); } private void showProvider() throws Exception { @@ -118,9 +102,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest waitForPostedSwingRunnables(); runSwing(() -> tool.showConfig(false, false)); - provider = tool.getManagePluginsDialog(); - pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider); - pluginModel = (PluginConfigurationModel) getInstanceField("model", pluginManagerComponent); - + managePluginsDialog = tool.getManagePluginsDialog(); + pluginModel = managePluginsDialog.getPluginConfigurationModel(); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java new file mode 100644 index 0000000000..e5087e50b5 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePlugins2Test.java @@ -0,0 +1,480 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool.dialog; + +import static org.hamcrest.collection.IsEmptyCollection.*; +import static org.hamcrest.core.IsNot.*; +import static org.junit.Assert.*; + +import java.util.*; +import java.util.stream.Collectors; + +import org.junit.*; + +import ghidra.app.plugin.core.help.AboutProgramPlugin; +import ghidra.app.plugin.core.totd.TipOfTheDayPlugin; +import ghidra.app.plugin.debug.JavaHelpPlugin; +import ghidra.framework.plugintool.*; +import ghidra.framework.plugintool.util.*; +import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.test.TestEnv; +import resources.ResourceManager; + +/** + * Tests for the configuring a tool. + * + * + */ +public class ManagePlugins2Test extends AbstractGhidraHeadedIntegrationTest { + + private TestEnv env; + private PluginTool tool; + private ManagePluginsDialog dialog; + + @Before + public void setUp() throws Exception { + env = new TestEnv(); + tool = env.getTool(); + } + + @After + public void tearDown() throws Exception { + close(dialog); + env.dispose(); + + sleep(1000); + } + + @Test + public void testPluginPackage_ReleasedPackage_ReleasedPlugins() throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + // + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage).build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage).build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + // test the checkbox to add/remove all plugins in the package + assertPackageCount(1); + assertPluginCount(testPackage, 2); + + selectAddAll(testPackage, true); + assertLoadedPlugins(stubInstaller, pd1, pd2); + + selectAddAll(testPackage, false); + assertNoPluginsLoaded(stubInstaller); + } + + @Test + public void testPluginPackage_ReleasedPackage_ReleasedAndStablePlugins() throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + // + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage) + .status(PluginStatus.RELEASED) + .build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + // test the checkbox to add/remove all plugins in the package + assertPackageCount(1); + assertPluginCount(testPackage, 2); + + selectAddAll(testPackage, true); + assertLoadedPlugins(stubInstaller, pd1); // only 1 'released' plugin loaded + + selectAddAll(testPackage, false); + assertNoPluginsLoaded(stubInstaller); + } + + @Test + public void testPluginPackage_StablePackage_ReleasedAndStablePlugins() throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + @Override + public PluginStatus getActivationLevel() { + return PluginStatus.STABLE; + } + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage) + .status(PluginStatus.RELEASED) + .build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + // test the checkbox to add/remove all plugins in the package + assertPackageCount(1); + assertPluginCount(testPackage, 2); + + selectAddAll(testPackage, true); + assertLoadedPlugins(stubInstaller, pd1, pd2); // 'released' and 'stable' plugins loaded + + selectAddAll(testPackage, false); + assertNoPluginsLoaded(stubInstaller); + } + + @Test + public void testPluginPackage_StablePackage_ReleasedAndStableAndUnstablePlugins() + throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + @Override + public PluginStatus getActivationLevel() { + return PluginStatus.STABLE; + } + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage) + .status(PluginStatus.RELEASED) + .build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + + StubPluginDescription pd3 = + new StubPluginDescriptionBuilder(TipOfTheDayPlugin.class, testPackage) + .status(PluginStatus.UNSTABLE) + .build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + stubProvider.addDescription(pd3); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + // test the checkbox to add/remove all plugins in the package + assertPackageCount(1); + assertPluginCount(testPackage, 3); + + selectAddAll(testPackage, true); + assertLoadedPlugins(stubInstaller, pd1, pd2); // 'released' and 'stable' plugins loaded + + selectAddAll(testPackage, false); + assertNoPluginsLoaded(stubInstaller); + } + + @Test + public void testPluginPackage_ReleasedPackage_StablePlugins() throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + // + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + // test the checkbox to add/remove all plugins in the package + assertPackageCount(1); + assertPluginCount(testPackage, 2); + + // the checkbox is disabled since there are no plugins that meet the activation level + assertAddAllCheckBoxDisabled(testPackage); + } + + @Test + public void testPluginPackageConfigure_ReleasedPackage_ReleasedAndStablePlugins() + throws Exception { + + PluginPackage testPackage = new PluginPackage("TEST", + ResourceManager.loadImage("images/Data_32.png"), "Test plugin pacakge") { + // + }; + + StubPluginDescription pd1 = + new StubPluginDescriptionBuilder(AboutProgramPlugin.class, testPackage) + .status(PluginStatus.RELEASED) + .build(); + StubPluginDescription pd2 = + new StubPluginDescriptionBuilder(JavaHelpPlugin.class, testPackage) + .status(PluginStatus.STABLE) + .build(); + + StubPluginPackagingProvider stubProvider = new StubPluginPackagingProvider(); + stubProvider.addPackage(testPackage); + stubProvider.addDescription(pd1); + stubProvider.addDescription(pd2); + + StubPluginInstaller stubInstaller = new StubPluginInstaller(); + PluginConfigurationModel model = new PluginConfigurationModel(stubInstaller, stubProvider); + + dialog = new ManagePluginsDialog(tool, model, false, false); + runSwing(() -> tool.showDialog(dialog), false); + + waitForDialogComponent(ManagePluginsDialog.class); + + PluginInstallerDialog installerDialog = pressConfigure(testPackage); + assertPluginCount(installerDialog, 2); + close(installerDialog); + } + +//================================================================================================== +// Private Methods +//================================================================================================== + + private void selectAddAll(PluginPackage pluginPackage, boolean select) { + PluginManagerComponent component = dialog.getPluginComponent(); + component.selectPluginPackage(pluginPackage, select); + } + + private void assertPackageCount(int expected) { + int actual = dialog.getPackageCount(); + assertEquals(expected, actual); + } + + private void assertPluginCount(PluginPackage pluginPackage, int expected) { + int actual = dialog.getPluginCount(pluginPackage); + assertEquals(expected, actual); + } + + private void assertLoadedPlugins(StubPluginInstaller stubInstaller, + StubPluginDescription... descriptions) { + + Set addedPlugins = stubInstaller.getAddedPlugins(); + assertEquals(descriptions.length, addedPlugins.size()); + for (PluginDescription pd : descriptions) { + assertTrue(addedPlugins.contains(pd.getPluginClass().getName())); + } + } + + private void assertNoPluginsLoaded(StubPluginInstaller installer) { + assertTrue(installer.getManagedPlugins().isEmpty()); + } + + private void assertAddAllCheckBoxDisabled(PluginPackage pluginPackage) { + PluginManagerComponent component = dialog.getPluginComponent(); + assertTrue(runSwing(() -> component.isAddAllCheckBoxEnabled(pluginPackage))); + } + + private PluginInstallerDialog pressConfigure(PluginPackage pluginPackage) { + PluginManagerComponent component = dialog.getPluginComponent(); + runSwing(() -> component.managePlugins(pluginPackage), false); + return waitForDialogComponent(PluginInstallerDialog.class); + } + + private void assertPluginCount(PluginInstallerDialog installerDialog, int expected) { + PluginConfigurationModel model = installerDialog.getModel(); + assertEquals(expected, model.getAllPluginDescriptions().size()); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + private class StubPluginInstaller implements PluginInstaller { + + private List loadedPlugins = new ArrayList<>(); + private Set addedPlugins = new HashSet<>(); + private Set removedPlugins = new HashSet<>(); + + @Override + public void addPlugins(List pluginClassNames) throws PluginException { + for (String name : pluginClassNames) { + addedPlugins.add(name); + runSwing(() -> loadPlugin(name)); + } + } + + private void loadPlugin(String name) { + try { + Class clazz = Class.forName(name); + Plugin plugin = + (Plugin) clazz.getDeclaredConstructor(PluginTool.class).newInstance(tool); + loadedPlugins.add(plugin); + } + catch (Exception e) { + failWithException("Unable to load plugin: " + name, e); + } + } + + @Override + public void removePlugins(List array) { + for (Plugin plugin : array) { + loadedPlugins.remove(plugin); + removedPlugins.remove(plugin.getClass().getSimpleName()); + } + } + + @Override + public List getManagedPlugins() { + return loadedPlugins; + } + + Set getAddedPlugins() { + return addedPlugins; + } + } + + private class StubPluginPackagingProvider implements PluginPackagingProvider { + + private List packages = new ArrayList<>(); + private List descriptions = new ArrayList<>(); + private List unstableDescriptions = new ArrayList<>(); + + @Override + public List getPluginPackages() { + return packages; + } + + void addPackage(PluginPackage p) { + packages.add(p); + } + + @Override + public List getPluginDescriptions() { + return descriptions; + } + + void addDescription(PluginDescription pd) { + descriptions.add(pd); + } + + @Override + public PluginDescription getPluginDescription(String pluginClassName) { + List results = descriptions.stream() + .filter(pd -> pd.getPluginClass().getName().equals(pluginClassName)) + .collect(Collectors.toList()); + assertEquals(1, results.size()); + return results.get(0); + } + + @Override + public List getPluginDescriptions(PluginPackage pluginPackage) { + List results = descriptions.stream() + .filter(pd -> pd.getPluginPackage().equals(pluginPackage)) + .collect(Collectors.toList()); + assertThat(results, not(empty())); + return results; + } + + @Override + public PluginPackage getUnstablePluginPackage() { + return UNSTABLE_PACKAGE; + } + + @Override + public List getUnstablePluginDescriptions() { + return unstableDescriptions; + } + } + + /* + + testPluginPackage_Released() { + + Click to add all + + Click to remove All + + } + + + testPluginPackage_Stable_ReleasedPlugins() + + testPluginPackage_Stable_ReleasedAndStablePlugins() + + testPluginPackage_Stable_ReleasedAndStableAndUnstablePlugins() + -unstable goes to 'experimental' + + testPluginPackage + + */ + +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java index d1c9cadb56..ec6385f1a6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/plugintool/dialog/ManagePluginsTest.java @@ -43,7 +43,6 @@ import ghidra.app.plugin.core.programtree.ProgramTreePlugin; import ghidra.app.plugin.debug.DbViewerPlugin; import ghidra.app.plugin.debug.EventDisplayPlugin; import ghidra.app.plugin.prototype.debug.ScreenshotPlugin; -import ghidra.framework.model.ToolChest; import ghidra.framework.plugintool.PluginConfigurationModel; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginDescription; @@ -60,7 +59,7 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { private TestEnv env; private PluginTool tool; - private ManagePluginsDialog provider; + private ManagePluginsDialog managePluginsDialog; private PluginConfigurationModel pluginModel; private String descrText; private PluginManagerComponent pluginManagerComponent; @@ -89,19 +88,17 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { @After public void tearDown() throws Exception { tool.setConfigChanged(false); - ToolChest tc = tool.getProject().getLocalToolChest(); - tc.remove("MyTestTool"); - runSwing(() -> provider.close()); - closeAllWindowsAndFrames(); + runSwing(() -> managePluginsDialog.close()); + closeAllWindows(); env.dispose(); } @Test public void testActionEnablement() { - performAction(provider.getSaveAction(), true); + performAction(managePluginsDialog.getSaveAction(), true); - assertTrue(!provider.getSaveAction().isEnabled()); - assertTrue(provider.getSaveAsAction().isEnabled()); + assertFalse(managePluginsDialog.getSaveAction().isEnabled()); + assertTrue(managePluginsDialog.getSaveAsAction().isEnabled()); } @Test @@ -114,11 +111,11 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { PluginDescription eventDisplay = PluginDescription.getPluginDescription(EventDisplayPlugin.class); - assertTrue(!pluginModel.isLoaded(dBViewer)); - assertTrue(!pluginModel.isLoaded(screenshot)); - assertTrue(!pluginModel.isLoaded(eventDisplay)); + assertFalse(pluginModel.isLoaded(dBViewer)); + assertFalse(pluginModel.isLoaded(screenshot)); + assertFalse(pluginModel.isLoaded(eventDisplay)); - executeOnSwingWithoutBlocking(() -> pluginModel.addAllPlugins(pluginPackage)); + executeOnSwingWithoutBlocking(() -> pluginModel.addSupportedPlugins(pluginPackage)); assertTrue(pluginModel.isLoaded(dBViewer)); assertTrue(pluginModel.isLoaded(screenshot)); @@ -126,9 +123,9 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { executeOnSwingWithoutBlocking(() -> pluginModel.removeAllPlugins(pluginPackage)); - assertTrue(!pluginModel.isLoaded(dBViewer)); - assertTrue(!pluginModel.isLoaded(screenshot)); - assertTrue(!pluginModel.isLoaded(eventDisplay)); + assertFalse(pluginModel.isLoaded(dBViewer)); + assertFalse(pluginModel.isLoaded(screenshot)); + assertFalse(pluginModel.isLoaded(eventDisplay)); } @@ -147,7 +144,7 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { waitForTasks(); assertTrue(tool.hasConfigChanged()); - assertTrue(provider.getSaveAction().isEnabled()); + assertTrue(managePluginsDialog.getSaveAction().isEnabled()); assertTrue( pluginModel.isLoaded(PluginDescription.getPluginDescription(AboutProgramPlugin.class))); } @@ -158,9 +155,9 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { SwingUtilities.invokeLater(() -> pluginManagerComponent.manageAllPlugins()); pluginModel.removePlugin(PluginDescription.getPluginDescription(EquateTablePlugin.class)); assertTrue(tool.hasConfigChanged()); - assertTrue(provider.getSaveAction().isEnabled()); - assertTrue(!pluginModel.isLoaded( - PluginDescription.getPluginDescription(AboutProgramPlugin.class))); + assertTrue(managePluginsDialog.getSaveAction().isEnabled()); + assertFalse( + pluginModel.isLoaded(PluginDescription.getPluginDescription(AboutProgramPlugin.class))); } @@ -238,14 +235,14 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { public void testSaveChanges() throws Exception { tool.setConfigChanged(true); assertTrue(tool.hasConfigChanged()); - performAction(provider.getSaveAction(), true); - assertTrue(!tool.hasConfigChanged()); + performAction(managePluginsDialog.getSaveAction(), true); + assertFalse(tool.hasConfigChanged()); } @Test public void testSaveAs() throws Exception { // verify the Save tool config dialog is displayed. - performAction(provider.getSaveAsAction(), false); + performAction(managePluginsDialog.getSaveAsAction(), false); waitForSwing(); SaveToolConfigDialog d = waitForDialogComponent(SaveToolConfigDialog.class); assertNotNull(d); @@ -259,12 +256,13 @@ public class ManagePluginsTest extends AbstractGhidraHeadedIntegrationTest { DockingActionIf action = getAction(tool, ToolConstants.TOOL_OWNER, "Configure Tool"); performAction(action, true); waitForSwing(); - provider = tool.getManagePluginsDialog(); - pluginManagerComponent = (PluginManagerComponent) getInstanceField("comp", provider); + managePluginsDialog = tool.getManagePluginsDialog(); + pluginManagerComponent = + (PluginManagerComponent) getInstanceField("pluginComponent", managePluginsDialog); executeOnSwingWithoutBlocking(() -> pluginManagerComponent.manageAllPlugins()); installerProvider = waitForDialogComponent(PluginInstallerDialog.class); - pluginModel = installerProvider.getModel(); + pluginModel = managePluginsDialog.getPluginConfigurationModel(); } } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidDebugPlugin.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidDebugPlugin.java index f2cd66eb22..217ccf192a 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidDebugPlugin.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidDebugPlugin.java @@ -70,7 +70,7 @@ import ghidra.util.task.TaskMonitor; //@formatter:on public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { - private static final String FUNCTION_ID_DEBUG_NAME = "Function ID Debug"; + static final String FUNCTION_ID_NAME = "Function ID"; private static final String MENU_GROUP_2 = "group2"; private FidService service; @@ -132,21 +132,19 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { } }; action.setMenuBarData(new MenuData( - new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, "Debug Search Window" }, + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Debug Search Window" }, null, MENU_GROUP_2, MenuData.NO_MNEMONIC, "7")); action.setDescription("Open a window to search the FID DBs in debug mode"); - action.setHelpLocation( - new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); + action.setHelpLocation(new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); tool.addAction(action); action = new HashAction(this); action.setMenuBarData(new MenuData( - new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Debug Search Window (Current Function)" }, null, MENU_GROUP_2, MenuData.NO_MNEMONIC, "1")); action.setDescription("FidHashes the current function for debug purposes"); - action.setHelpLocation( - new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); + action.setHelpLocation(new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); tool.addAction(action); createRawFileAction = new DockingAction("raw file", getName()) { @@ -155,13 +153,15 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { createRawFile(); } }; - createRawFileAction.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, - "Create Read-only Database" }, null, MENU_GROUP_2, MenuData.NO_MNEMONIC, "1")); + createRawFileAction + .setMenuBarData(new MenuData( + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, + "Create Read-only Database" }, + null, MENU_GROUP_2, MenuData.NO_MNEMONIC, "1")); createRawFileAction.setDescription( "Creates a raw read-only database suitable for distribution in the installation directory"); - createRawFileAction.setHelpLocation( - new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); + createRawFileAction + .setHelpLocation(new HelpLocation(FidPlugin.FID_HELP, "FunctionIDDebug")); tool.addAction(createRawFileAction); } @@ -222,8 +222,8 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { List componentProviders = tool.getWindowManager().getComponentProviders(FidDbViewerProvider.class); for (ComponentProvider comp : componentProviders) { - if (((FidDbViewerProvider) comp).fidDB.getPath().equals( - fidFile.getPath())) { + if (((FidDbViewerProvider) comp).fidDB.getPath() + .equals(fidFile.getPath())) { tool.getWindowManager().showComponent(comp, true); return; } @@ -239,12 +239,11 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { } }; action.setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_TOOLS, - FidPluginPackage.NAME, "Table Viewer", fidFile.getName() }, null, MENU_GROUP_2, + FUNCTION_ID_NAME, "Table Viewer", fidFile.getName() }, null, MENU_GROUP_2, MenuData.NO_MNEMONIC, "6")); action.setDescription( "Opens new DB Table Viewer to browse the database in " + fidFile.getName()); - action.setHelpLocation( - new HelpLocation(FidPlugin.FID_HELP, "FunctionIDPlugin")); + action.setHelpLocation(new HelpLocation(FidPlugin.FID_HELP, "FunctionIDPlugin")); action.setEnabled(true); if (tool != null) { tool.addAction(action); @@ -253,8 +252,7 @@ public class FidDebugPlugin extends ProgramPlugin implements ChangeListener { } // make these appear at the bottom of the Fid Menu. tool.setMenuGroup( - new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, "Table Viewer" }, - "zzz"); + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Table Viewer" }, "zzz"); } diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java index 73c0623779..168c2bb3ad 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPlugin.java @@ -28,6 +28,8 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.tool.ToolConstants; import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.script.AskDialog; @@ -39,7 +41,6 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.util.HelpLocation; import ghidra.util.Msg; -import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateFileException; /** @@ -51,17 +52,16 @@ import ghidra.util.exception.DuplicateFileException; //@formatter:off @PluginInfo( status = PluginStatus.RELEASED, - packageName = FidPluginPackage.NAME, + packageName = CorePluginPackage.NAME, category = PluginCategoryNames.SEARCH, shortDescription = FidPlugin.FUNCTION_ID_NAME, description = "This plugin is for creating and maintaining function identification libraries." ) //@formatter:on public class FidPlugin extends ProgramPlugin implements ChangeListener { + private static final String MENU_GROUP_1 = "group1"; - static final String FUNCTION_ID_NAME = "Function ID"; - public static final String FID_HELP = "FunctionID"; private FidService service; @@ -115,9 +115,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { chooseActiveFidDbs(); } }; - action.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, - "Choose active FidDbs..." }, null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "1")); + action.setMenuBarData(new MenuData( + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Choose active FidDbs..." }, + null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "1")); action.setDescription("Select which FidDbs are used during Fid Search"); action.setHelpLocation(new HelpLocation(FID_HELP, "chooseactivemenu")); tool.addAction(action); @@ -130,7 +130,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { } }; action.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, + new MenuData(new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Create new empty FidDb..." }, null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "2")); action.setDescription("Create a new, empty FidDb file in your file system"); action.setHelpLocation(new HelpLocation(FID_HELP, "createemptyfid")); @@ -143,9 +143,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { attachFidDb(); } }; - action.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, - "Attach existing FidDb..." }, null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "3")); + action.setMenuBarData(new MenuData( + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Attach existing FidDb..." }, + null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "3")); action.setDescription("Attach an existing FidDb file from your file system"); action.setHelpLocation(new HelpLocation(FID_HELP, "attachfid")); tool.addAction(action); @@ -157,9 +157,9 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { removeFidFile(); } }; - action.setMenuBarData( - new MenuData(new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, - "Detach attached FidDb..." }, null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "4")); + action.setMenuBarData(new MenuData( + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Detach attached FidDb..." }, + null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "4")); action.setDescription("Detach an already attached FidDb"); action.setHelpLocation(new HelpLocation(FID_HELP, "detachfid")); tool.addAction(action); @@ -173,7 +173,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { } }; action.setMenuBarData(new MenuData( - new String[] { ToolConstants.MENU_TOOLS, FidPluginPackage.NAME, + new String[] { ToolConstants.MENU_TOOLS, FUNCTION_ID_NAME, "Populate FidDb from programs..." }, null, MENU_GROUP_1, MenuData.NO_MNEMONIC, "5")); action.setDescription("Populate an existing FidDb with all programs under a domain folder"); @@ -266,7 +266,7 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { final GhidraFileChooser chooser = new GhidraFileChooser(tool.getActiveWindow()); chooser.setApproveButtonText(approveButtonText); chooser.setTitle(title); - chooser.setFileSelectionMode(GhidraFileChooser.FILES_ONLY); + chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY); return chooser.getSelectedFile(); } @@ -277,7 +277,6 @@ public class FidPlugin extends ProgramPlugin implements ChangeListener { * @param choices array of choices for the users * @param defaultValue the default value to select * @return the user's choice, or null - * @throws CancelledException if the user cancels */ private T askChoice(String title, String message, List choices, T defaultValue) { AskDialog dialog = diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPluginPackage.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPluginPackage.java deleted file mode 100644 index 977c831493..0000000000 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/plugin/FidPluginPackage.java +++ /dev/null @@ -1,31 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.feature.fid.plugin; - -import ghidra.framework.plugintool.util.PluginPackage; -import resources.ResourceManager; - -/** - * Plugin package for the FID plugin. - */ -public class FidPluginPackage extends PluginPackage { - public static final String NAME = "Function ID"; - - public FidPluginPackage() { - super(NAME, ResourceManager.loadImage("images/vcard.png"), - "These plugins are for creating, maintaining, and debugging function identification libraries."); - } -} diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DockingDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/DockingDialog.java index 16203eb0b3..c47a95c333 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DockingDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DockingDialog.java @@ -55,13 +55,13 @@ public class DockingDialog extends JDialog implements HelpDescriptor { /** * Creates a default parent frame that will appear in the OS's task bar. Having this frame - * gives the user something to click when their dialog is lost. We attempt to hide this + * gives the user something to click when their dialog is lost. We attempt to hide this * frame offscreen. - * + * * Note: we expect to only get here when there is no parent window found. This usually - * only happens during tests and one-off main methods that are not part of a + * only happens during tests and one-off main methods that are not part of a * running tool. - * + * * @param componentProvider the dialog content for this dialog * @return the hidden frame */ @@ -69,7 +69,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor { // // Note: we expect to only get here when there is no parent window found. This usually - // only happens during tests and one-off main methods that are not part of a + // only happens during tests and one-off main methods that are not part of a // running tool // HiddenDockingFrame hiddenFrame = new HiddenDockingFrame(Application.getName()); @@ -132,8 +132,8 @@ public class DockingDialog extends JDialog implements HelpDescriptor { Point initialLocation = component.getIntialLocation(); if (initialLocation != null) { - // NOTE: have to call setLocation() twice because the first time the native peer - // component's location is not actually changed; calling setLocation() again + // NOTE: have to call setLocation() twice because the first time the native peer + // component's location is not actually changed; calling setLocation() again // does cause the location to change. setLocation(initialLocation); setLocation(initialLocation); @@ -286,7 +286,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor { JFrame f = (JFrame) myParent; Window[] ownedWindows = f.getOwnedWindows(); for (Window window : ownedWindows) { - if (window != this) { + if (window != this && window.isVisible()) { return; } } @@ -382,7 +382,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor { void setEndBounds(Rectangle bounds) { if (Objects.equals(startBounds, bounds)) { - // keep the end bounds unchanged, which helps us later determine if the + // keep the end bounds unchanged, which helps us later determine if the // dialog was moved return; } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DeafultPluginPackagingProvider.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DeafultPluginPackagingProvider.java new file mode 100644 index 0000000000..05ba901de1 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DeafultPluginPackagingProvider.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool; + +import java.util.List; + +import ghidra.framework.plugintool.util.*; + +/** + * The default plugin package provider that uses the {@link PluginClassManager} to supply packages + */ +public class DeafultPluginPackagingProvider implements PluginPackagingProvider { + + private PluginClassManager pluginClassManager; + + DeafultPluginPackagingProvider(PluginClassManager pluginClassManager) { + this.pluginClassManager = pluginClassManager; + } + + @Override + public List getPluginPackages() { + return pluginClassManager.getPluginPackages(); + } + + @Override + public List getPluginDescriptions() { + return pluginClassManager.getManagedPluginDescriptions(); + } + + @Override + public PluginDescription getPluginDescription(String pluginClassName) { + return pluginClassManager.getPluginDescription(pluginClassName); + } + + @Override + public List getPluginDescriptions(PluginPackage pluginPackage) { + return pluginClassManager.getPluginDescriptions(pluginPackage); + } + + @Override + public PluginPackage getUnstablePluginPackage() { + return UNSTABLE_PACKAGE; + } + + @Override + public List getUnstablePluginDescriptions() { + return pluginClassManager.getUnstablePluginDescriptions(); + } + +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DefaultPluginInstaller.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DefaultPluginInstaller.java new file mode 100644 index 0000000000..546b5afd02 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/DefaultPluginInstaller.java @@ -0,0 +1,47 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool; + +import java.util.List; + +import ghidra.framework.plugintool.util.PluginException; + +/** + * The default plugin installer that uses a tool to install plugins + */ +public class DefaultPluginInstaller implements PluginInstaller { + + private PluginTool tool; + + DefaultPluginInstaller(PluginTool tool) { + this.tool = tool; + } + + @Override + public List getManagedPlugins() { + return tool.getManagedPlugins(); + } + + @Override + public void addPlugins(List pluginClassNames) throws PluginException { + tool.addPlugins(pluginClassNames); + } + + @Override + public void removePlugins(List plugins) { + tool.removePlugins(plugins); + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java index df39ec4738..5dc3c283f0 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginConfigurationModel.java @@ -17,63 +17,54 @@ package ghidra.framework.plugintool; import java.util.*; -import javax.swing.Icon; -import javax.swing.event.ChangeListener; - -import docking.action.DockingActionIf; -import docking.actions.KeyBindingUtils; import ghidra.framework.plugintool.util.*; import ghidra.util.Msg; -import resources.ResourceManager; +import utility.function.Callback; +import utility.function.Dummy; public class PluginConfigurationModel { - private static Icon EXPERIMENTAL_ICON = - ResourceManager.loadImage("images/applications-science.png"); - private final ChangeListener listener; - private final PluginTool tool; - private PluginClassManager pluginClassManager; + + private final PluginInstaller pluginInstaller; + private PluginPackagingProvider pluginPackagingProvider; + private Callback listener = Callback.dummy(); private Map loadedPluginMap = new HashMap<>(); private Set pluginsWithDependenciesSet = new HashSet<>(); private List unStablePluginDescriptions; private PluginPackage unstablePackage; public PluginConfigurationModel(PluginTool tool) { - this(tool, e -> { - // dummy listener - }); + this(new DefaultPluginInstaller(tool), + new DeafultPluginPackagingProvider(tool.getPluginClassManager())); } - public PluginConfigurationModel(PluginTool tool, ChangeListener listener) { - this.tool = tool; - this.listener = listener; - pluginClassManager = tool.getPluginClassManager(); + public PluginConfigurationModel(PluginInstaller pluginInstaller, + PluginPackagingProvider pluginPackagingProvider) { + + this.pluginInstaller = pluginInstaller; + this.pluginPackagingProvider = pluginPackagingProvider; initLoadedPlugins(); - unStablePluginDescriptions = pluginClassManager.getNonReleasedPluginDescriptions(); - if (!unStablePluginDescriptions.isEmpty()) { - unstablePackage = new PluginPackage("Experimental", EXPERIMENTAL_ICON, - "This package contains plugins that are not fully tested and/or documented." + - "You must add these plugins individually. Adding these plugins could cause the tool" + - " to become unstable.", - Integer.MAX_VALUE) { - @Override - public boolean isfullyAddable() { - return false; - } - }; - } + + unstablePackage = pluginPackagingProvider.getUnstablePluginPackage(); + unStablePluginDescriptions = pluginPackagingProvider.getUnstablePluginDescriptions(); + } + + public void setChangeCallback(Callback listener) { + this.listener = Dummy.ifNull(listener); } public List getPluginPackages() { - List pluginPackages = pluginClassManager.getPluginPackages(); + List pluginPackages = pluginPackagingProvider.getPluginPackages(); List packagesWithStablePlugins = new ArrayList<>(); for (PluginPackage pluginPackage : pluginPackages) { - if (pluginClassManager.getReleasedPluginDescriptions(pluginPackage).size() > 0) { + if (pluginPackagingProvider.getPluginDescriptions(pluginPackage).size() > 0) { packagesWithStablePlugins.add(pluginPackage); } } - if (unstablePackage != null) { + + if (!unStablePluginDescriptions.isEmpty()) { packagesWithStablePlugins.add(unstablePackage); } + return packagesWithStablePlugins; } @@ -81,17 +72,17 @@ public class PluginConfigurationModel { if (pluginPackage == unstablePackage) { return unStablePluginDescriptions; } - return pluginClassManager.getReleasedPluginDescriptions(pluginPackage); + return pluginPackagingProvider.getPluginDescriptions(pluginPackage); } /** - * Gets the loaded plugins from the tool and populates the loadedPluginMap and the - * pluginsWithDependenciesSet. + * Gets the loaded plugins from the tool and populates the loadedPluginMap and the + * pluginsWithDependenciesSet. */ private void initLoadedPlugins() { loadedPluginMap.clear(); pluginsWithDependenciesSet.clear(); - List list = tool.getManagedPlugins(); + List list = pluginInstaller.getManagedPlugins(); for (Plugin plugin : list) { loadedPluginMap.put(getPluginDescription(plugin), plugin); findDependencies(plugin, list); @@ -104,8 +95,7 @@ public class PluginConfigurationModel { * @param plugins the list of all loaded plugins. */ private void findDependencies(Plugin plugin, List plugins) { - for (int i = 0; i < plugins.size(); i++) { - Plugin p = plugins.get(i); + for (Plugin p : plugins) { if (p.dependsUpon(plugin)) { pluginsWithDependenciesSet.add(getPluginDescription(plugin)); } @@ -114,7 +104,7 @@ public class PluginConfigurationModel { private PluginDescription getPluginDescription(Plugin plugin) { String className = plugin.getClass().getName(); - return pluginClassManager.getPluginDescription(className); + return pluginPackagingProvider.getPluginDescription(className); } public boolean isLoaded(PluginDescription pluginDescription) { @@ -145,13 +135,14 @@ public class PluginConfigurationModel { public void addPlugin(PluginDescription pluginDescription) { try { - tool.addPlugin(pluginDescription.getPluginClass().getName()); + String name = pluginDescription.getPluginClass().getName(); + pluginInstaller.addPlugins(Arrays.asList(name)); } catch (PluginException e) { Msg.showError(this, null, "Error Loading Plugin", e.getMessage(), e); } initLoadedPlugins(); - listener.stateChanged(null); + listener.call(); } public void removeAllPlugins(PluginPackage pluginPackage) { @@ -162,37 +153,55 @@ public class PluginConfigurationModel { loadedPlugins.add(loadedPluginMap.get(pluginDescription)); } } - tool.removePlugins(loadedPlugins.toArray(new Plugin[loadedPlugins.size()])); + pluginInstaller.removePlugins(loadedPlugins); initLoadedPlugins(); - listener.stateChanged(null); + listener.call(); } - public void addAllPlugins(PluginPackage pluginPackage) { - List pluginDescriptions = getPluginDescriptions(pluginPackage); + public void addSupportedPlugins(PluginPackage pluginPackage) { + PluginStatus activationLevel = pluginPackage.getActivationLevel(); + List pluginDescriptions = getPluginDescriptions(pluginPackage); List pluginClasseNames = new ArrayList<>(); for (PluginDescription pluginDescription : pluginDescriptions) { + + PluginStatus status = pluginDescription.getStatus(); + if (status.compareTo(activationLevel) > 0) { + continue; // status is not good enough to be activated (e.g., UNSTABLE) + } + if (!isLoaded(pluginDescription)) { pluginClasseNames.add(pluginDescription.getPluginClass().getName()); } } try { - tool.addPlugins(pluginClasseNames.toArray(new String[pluginClasseNames.size()])); + pluginInstaller.addPlugins(pluginClasseNames); } catch (PluginException e) { Msg.showError(this, null, "Error Loading Plugin(s) ", e.getMessage(), e); } initLoadedPlugins(); - listener.stateChanged(null); + listener.call(); + } + + public boolean hasOnlyUnstablePlugins(PluginPackage pluginPackage) { + List pluginDescriptions = getPluginDescriptions(pluginPackage); + for (PluginDescription pluginDescription : pluginDescriptions) { + PluginStatus status = pluginDescription.getStatus(); + if (status.compareTo(PluginStatus.UNSTABLE) < 0) { + return false; + } + } + return true; } public void removePlugin(PluginDescription pluginDescription) { Plugin plugin = loadedPluginMap.get(pluginDescription); if (plugin != null) { - tool.removePlugins(new Plugin[] { plugin }); + pluginInstaller.removePlugins(Arrays.asList(plugin)); } initLoadedPlugins(); - listener.stateChanged(null); + listener.call(); } /** @@ -207,28 +216,15 @@ public class PluginConfigurationModel { } /** - * Returns all of the actions loaded by the Plugin represented by the given PluginDescription. - * An empty list will be returned if no actions are loaded or if the plugin has not been - * loaded. - * @param pluginDescription The description for which to find loaded actions. - * @return all of the actions loaded by the Plugin represented by the given PluginDescription. - */ - public Set getActionsForPlugin(PluginDescription pluginDescription) { - if (!isLoaded(pluginDescription)) { - return Collections.emptySet(); - } - - return KeyBindingUtils.getKeyBindingActionsForOwner(tool, pluginDescription.getName()); - } - - /** - * Return the names of the plugins that are dependent on some service - * that the plugin corresponding to the given PluginDescription provides. + * Return the descriptions of the plugins that are dependent on some service that the plugin + * corresponding to the given PluginDescription provides. + * * @param pd PluginDescription of the plugin + * @return the descriptions */ public List getDependencies(PluginDescription pd) { Plugin plugin = loadedPluginMap.get(pd); - return (plugin != null) ? getDependencies(plugin, tool.getManagedPlugins()) + return (plugin != null) ? getDependencies(plugin, pluginInstaller.getManagedPlugins()) : Collections.emptyList(); } @@ -236,8 +232,7 @@ public class PluginConfigurationModel { HashSet set = new HashSet<>(); // find out all plugins that depend on this plugin - for (int i = 0; i < plugins.size(); i++) { - Plugin p = plugins.get(i); + for (Plugin p : plugins) { if (p.dependsUpon(plugin)) { set.add(p.getPluginDescription()); } @@ -246,7 +241,6 @@ public class PluginConfigurationModel { } public List getAllPluginDescriptions() { - return pluginClassManager.getAllPluginDescriptions(); + return pluginPackagingProvider.getPluginDescriptions(); } - } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginInstaller.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginInstaller.java new file mode 100644 index 0000000000..7d7a600503 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginInstaller.java @@ -0,0 +1,45 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool; + +import java.util.List; + +import ghidra.framework.plugintool.util.PluginException; + +/** + * An interface that facilitates the adding and removing of plugins + */ +public interface PluginInstaller { + + /** + * Returns all currently installed plugins + * @return the plugins + */ + public List getManagedPlugins(); + + /** + * Adds the given plugins to the system + * @param pluginClassNames the plugin class names to add + * @throws PluginException if there is an issue loading any of the plugins + */ + public void addPlugins(List pluginClassNames) throws PluginException; + + /** + * Removes the given plugins from the system + * @param plugins the plugins + */ + public void removePlugins(List plugins); +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginManager.java index 44e01da30c..0bce83e6b0 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginManager.java @@ -86,9 +86,9 @@ class PluginManager { addPlugins(new Plugin[] { plugin }); } - void addPlugins(String[] classNames) throws PluginException { + void addPlugins(List classNames) throws PluginException { PluginException pe = null; - List list = new ArrayList<>(classNames.length); + List list = new ArrayList<>(classNames.size()); List badList = new ArrayList<>(); for (String className : classNames) { try { @@ -115,8 +115,7 @@ class PluginManager { } if (badList.size() > 0) { //EventManager eventMgr = tool.getEventManager - for (int i = 0; i < badList.size(); i++) { - String className = badList.get(i); + for (String className : badList) { // remove from event manager tool.removeEventListener(className); } @@ -192,7 +191,7 @@ class PluginManager { if (badList.size() > 0) { Plugin[] badPlugins = new Plugin[badList.size()]; try { - removePlugins(badList.toArray(badPlugins)); + removePlugins(badList); } catch (Throwable t) { log.debug("Exception unloading plugin", t); @@ -229,7 +228,7 @@ class PluginManager { * depending on them. * @param plugins the list of plugins to remove. */ - void removePlugins(Plugin[] plugins) { + void removePlugins(List plugins) { for (Plugin plugin : plugins) { unregisterPlugin(plugin); } @@ -268,7 +267,7 @@ class PluginManager { PluginException pe = null; try { - addPlugins(classNames.toArray(new String[classNames.size()])); + addPlugins(classNames); } catch (PluginException e) { pe = e; @@ -367,8 +366,7 @@ class PluginManager { Element saveDataStateToXml(boolean savingProject) { Element root = new Element("DATA_STATE"); - for (int i = 0; i < pluginList.size(); i++) { - Plugin p = pluginList.get(i); + for (Plugin p : pluginList) { SaveState ss = new SaveState("PLUGIN"); p.writeDataState(ss); if (!ss.isEmpty()) { @@ -518,10 +516,7 @@ class PluginManager { } catch (Exception e) { errMsg.append("Problem restoring plugin state for: " + p.getName()).append("\n\n"); - errMsg.append(e.getClass().getName()) - .append(": ") - .append(e.getMessage()) - .append('\n'); + errMsg.append(e.getClass().getName()).append(": ").append(e.getMessage()).append('\n'); StackTraceElement[] st = e.getStackTrace(); int depth = Math.min(5, st.length); // only show the important stuff (magic guess) for (int j = 0; j < depth; j++) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginPackagingProvider.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginPackagingProvider.java new file mode 100644 index 0000000000..c6344e9f71 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginPackagingProvider.java @@ -0,0 +1,78 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool; + +import java.util.List; + +import javax.swing.Icon; + +import ghidra.framework.plugintool.util.*; +import resources.ResourceManager; + +/** + * Provides {@link PluginPackage}s and plugin descriptions and to clients + */ +public interface PluginPackagingProvider { + + public static final Icon EXPERIMENTAL_ICON = + ResourceManager.loadImage("images/applications-science.png"); + public static final PluginPackage UNSTABLE_PACKAGE = new PluginPackage("Experimental", + EXPERIMENTAL_ICON, + "This package contains plugins that are not fully tested and/or documented." + + "You must add these plugins individually. Adding these plugins could cause the tool" + + " to become unstable.", + Integer.MAX_VALUE) { + // + }; + + /** + * Returns all known plugin packages + * @return the plugin packages + */ + public List getPluginPackages(); + + /** + * Returns all loaded plugin descriptions + * @return the descriptions + */ + public List getPluginDescriptions(); + + /** + * Returns the plugin description for the given plugin class name + * @param pluginClassName the plugin class name + * @return the description + */ + public PluginDescription getPluginDescription(String pluginClassName); + + /** + * Get all plugin descriptions for the given plugin package + * @param pluginPackage the package + * @return the descriptions + */ + public List getPluginDescriptions(PluginPackage pluginPackage); + + /** + * Returns the plugin package used to house all unstable plugin packages + * @return the package + */ + public PluginPackage getUnstablePluginPackage(); + + /** + * Returns all {@link PluginStatus#UNSTABLE} plugin package descriptions + * @return the descriptions + */ + public List getUnstablePluginDescriptions(); +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java index 4cdea06ca9..8d2b710765 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginTool.java @@ -69,7 +69,7 @@ import ghidra.util.task.*; * an alternate method for getting actions to appear in the popup context menu (see * {@link #addPopupActionProvider(PopupActionProvider)}). The popup listener mechanism is generally not * needed and should only be used in special circumstances (see {@link PopupActionProvider}). - * + * *

The PluginTool also manages tasks that run in the background, and options used by the plugins. * */ @@ -807,8 +807,26 @@ public abstract class PluginTool extends AbstractDockingTool { * @throws PluginException if a plugin could not be constructed, or * there was problem executing its init() method, or if a plugin of this * class already exists in the tool + * @deprecated use {@link #addPlugins(List)} */ + @Deprecated(since = "10.2", forRemoval = true) public void addPlugins(String[] classNames) throws PluginException { + try { + pluginMgr.addPlugins(Arrays.asList(classNames)); + } + finally { + setConfigChanged(true); + } + } + + /** + * Add plugins to the tool. + * @param classNames array of plugin class names + * @throws PluginException if a plugin could not be constructed, or + * there was problem executing its init() method, or if a plugin of this + * class already exists in the tool + */ + public void addPlugins(List classNames) throws PluginException { try { pluginMgr.addPlugins(classNames); } @@ -822,15 +840,28 @@ public abstract class PluginTool extends AbstractDockingTool { setConfigChanged(true); } - public boolean hasUnsavedData() { - return pluginMgr.hasUnsavedData(); + /** + * Remove the array of plugins from the tool. + * @param plugins array of plugins to remove + * @deprecated use {@link #removePlugins(List)} + */ + @Deprecated(since = "10.2", forRemoval = true) + public void removePlugins(Plugin[] plugins) { + SystemUtilities.runSwingNow(() -> { + try { + pluginMgr.removePlugins(Arrays.asList(plugins)); + } + finally { + setConfigChanged(true); + } + }); } /** * Remove the array of plugins from the tool. * @param plugins array of plugins to remove */ - public void removePlugins(Plugin[] plugins) { + public void removePlugins(List plugins) { SystemUtilities.runSwingNow(() -> { try { pluginMgr.removePlugins(plugins); @@ -841,6 +872,10 @@ public abstract class PluginTool extends AbstractDockingTool { }); } + public boolean hasUnsavedData() { + return pluginMgr.hasUnsavedData(); + } + /** * Return a list of plugins in the tool * @return list of plugins in the tool @@ -960,8 +995,8 @@ public abstract class PluginTool extends AbstractDockingTool { saveAsAction.setMenuBarData(menuData); saveAsAction.setEnabled(true); - saveAsAction.setHelpLocation( - new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Tool_Changes")); + saveAsAction + .setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Tool_Changes")); addAction(saveAction); addAction(saveAsAction); @@ -986,8 +1021,8 @@ public abstract class PluginTool extends AbstractDockingTool { new String[] { ToolConstants.MENU_FILE, exportPullright, "Export Tool..." }); menuData.setMenuSubGroup(Integer.toString(subGroup++)); exportToolAction.setMenuBarData(menuData); - exportToolAction.setHelpLocation( - new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Export_Tool")); + exportToolAction + .setHelpLocation(new HelpLocation(ToolConstants.TOOL_HELP_TOPIC, "Export_Tool")); addAction(exportToolAction); DockingAction exportDefautToolAction = @@ -1135,7 +1170,7 @@ public abstract class PluginTool extends AbstractDockingTool { *
Note: This forces plugins to terminate any tasks they have running and * apply any unsaved data to domain objects or files. If they can't do * this or the user cancels then this returns false. - * + * * @param isExiting whether the tool is exiting * @return false if this tool has tasks in progress or can't be closed * since the user has unfinished/unsaved changes. @@ -1170,7 +1205,7 @@ public abstract class PluginTool extends AbstractDockingTool { *
Note: This forces plugins to terminate any tasks they have running for the * indicated domain object and apply any unsaved data to the domain object. If they can't do * this or the user cancels then this returns false. - * + * * @param domainObject the domain object to check * @return false any of the plugins reports that the domain object * should not be closed @@ -1361,7 +1396,7 @@ public abstract class PluginTool extends AbstractDockingTool { * time the dialog is shown. * * @param dialogComponent the DialogComponentProvider object to be shown in a dialog. - * + * * @deprecated dialogs are now always shown over the active window when possible */ @Deprecated diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/ManagePluginsDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/ManagePluginsDialog.java index 879c1810b3..22b0737b00 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/ManagePluginsDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/ManagePluginsDialog.java @@ -19,8 +19,6 @@ import java.awt.Color; import java.awt.Point; import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import docking.ActionContext; import docking.DialogComponentProvider; @@ -29,27 +27,34 @@ import docking.tool.ToolConstants; import docking.widgets.OptionDialog; import ghidra.app.util.GenericHelpTopics; import ghidra.framework.main.AppInfo; +import ghidra.framework.plugintool.PluginConfigurationModel; import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.PluginPackage; import ghidra.util.HelpLocation; -import ghidra.util.classfinder.ClassSearcher; import resources.ResourceManager; -public class ManagePluginsDialog extends DialogComponentProvider implements ChangeListener { +public class ManagePluginsDialog extends DialogComponentProvider { private PluginTool tool; private boolean isNewTool; private DockingAction saveAction; private DockingAction saveAsAction; private DockingAction configureAllPluginsAction; - private PluginManagerComponent comp; + private PluginManagerComponent pluginComponent; + private PluginConfigurationModel pluginConfigurationModel; public ManagePluginsDialog(PluginTool tool, boolean addSaveActions, boolean isNewTool) { + this(tool, new PluginConfigurationModel(tool), addSaveActions, isNewTool); + } + + public ManagePluginsDialog(PluginTool tool, PluginConfigurationModel pluginConfigurationModel, + boolean addSaveActions, boolean isNewTool) { super("Configure Tool", false, true, true, true); this.tool = tool; this.isNewTool = isNewTool; - ClassSearcher.addChangeListener(this); - comp = new PluginManagerComponent(tool); - JScrollPane scrollPane = new JScrollPane(comp); + this.pluginConfigurationModel = pluginConfigurationModel; + pluginComponent = new PluginManagerComponent(tool, pluginConfigurationModel); + JScrollPane scrollPane = new JScrollPane(pluginComponent); scrollPane.getViewport().setBackground(Color.white); scrollPane.getViewport().setViewPosition(new Point(0, 0)); addWorkPanel(scrollPane); @@ -90,7 +95,6 @@ public class ManagePluginsDialog extends DialogComponentProvider implements Chan save(); } } - ClassSearcher.removeChangeListener(this); close(); } @@ -99,14 +103,14 @@ public class ManagePluginsDialog extends DialogComponentProvider implements Chan new DockingAction("Configure All Plugins", ToolConstants.TOOL_OWNER) { @Override public void actionPerformed(ActionContext context) { - comp.manageAllPlugins(); + pluginComponent.manageAllPlugins(); } }; ImageIcon icon = ResourceManager.loadImage("images/plugin.png"); configureAllPluginsAction.setToolBarData(new ToolBarData(icon, "aaa")); configureAllPluginsAction.setDescription("Configure All Plugins"); - configureAllPluginsAction.setHelpLocation( - new HelpLocation(GenericHelpTopics.TOOL, "ConfigureAllPlugins")); + configureAllPluginsAction + .setHelpLocation(new HelpLocation(GenericHelpTopics.TOOL, "ConfigureAllPlugins")); addAction(configureAllPluginsAction); if (addSaveActions) { @@ -133,8 +137,8 @@ public class ManagePluginsDialog extends DialogComponentProvider implements Chan }; saveAsAction.setEnabled(true); icon = ResourceManager.loadImage("images/disk_save_as.png"); - saveAsAction.setMenuBarData( - new MenuData(new String[] { "Save As..." }, icon, saveGroup)); + saveAsAction + .setMenuBarData(new MenuData(new String[] { "Save As..." }, icon, saveGroup)); saveAsAction.setToolBarData(new ToolBarData(icon, saveGroup)); saveAsAction.setHelpLocation(new HelpLocation(GenericHelpTopics.TOOL, "SaveTool")); saveAsAction.setDescription("Save tool to new name in tool chest"); @@ -142,6 +146,10 @@ public class ManagePluginsDialog extends DialogComponentProvider implements Chan } } + public PluginConfigurationModel getPluginConfigurationModel() { + return pluginConfigurationModel; + } + private void save() { if (isNewTool) { saveAs(); @@ -158,15 +166,21 @@ public class ManagePluginsDialog extends DialogComponentProvider implements Chan isNewTool = false; } - @Override - public void stateChanged(ChangeEvent e) { - //comp.refresh(); - } - public void stateChanged() { if (saveAction != null) { saveAction.setEnabled(tool.hasConfigChanged()); } } + int getPackageCount() { + return pluginComponent.getPackageCount(); + } + + int getPluginCount(PluginPackage pluginPackage) { + return pluginComponent.getPluginCount(pluginPackage); + } + + PluginManagerComponent getPluginComponent() { + return pluginComponent; + } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java index 88f9586575..973306fb3d 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginDetailsPanel.java @@ -27,6 +27,7 @@ import docking.action.DockingActionIf; import docking.action.MenuData; import docking.actions.KeyBindingUtils; import ghidra.framework.plugintool.PluginConfigurationModel; +import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginDescription; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.util.HTMLUtilities; @@ -47,9 +48,11 @@ class PluginDetailsPanel extends AbstractDetailsPanel { private SimpleAttributeSet noValueAttrSet; private final PluginConfigurationModel model; + private PluginTool tool; - PluginDetailsPanel(PluginConfigurationModel model) { + PluginDetailsPanel(PluginTool tool, PluginConfigurationModel model) { super(); + this.tool = tool; this.model = model; createFieldAttributes(); createMainPanel(); @@ -158,8 +161,13 @@ class PluginDetailsPanel extends AbstractDetailsPanel { insertHTMLLine(buffer, "Loaded Actions:", titleAttrSet); buffer.append(""); - Set actions = model.getActionsForPlugin(pluginDescription); - if (actions.size() == 0) { + Set actions = Collections.emptySet(); + if (model.isLoaded(pluginDescription)) { + actions = + KeyBindingUtils.getKeyBindingActionsForOwner(tool, pluginDescription.getName()); + } + + if (actions.isEmpty()) { buffer.append(""); insertHTMLLine(buffer, "No actions for plugin", noValueAttrSet); buffer.append(""); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginInstallerDialog.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginInstallerDialog.java index 4291be7dd8..1c81c082d1 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginInstallerDialog.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginInstallerDialog.java @@ -48,22 +48,19 @@ public class PluginInstallerDialog extends DialogComponentProvider { /** * Constructs a new provider. - * + * * @param title the title of the provider * @param tool the current tool + * @param model the plugin configuration model * @param pluginDescriptions the list of plugins to display in the dialog */ - public PluginInstallerDialog(String title, PluginTool tool, + public PluginInstallerDialog(String title, PluginTool tool, PluginConfigurationModel model, List pluginDescriptions) { super(title, true, false, true, false); this.tool = tool; - - if (model == null) { - model = new PluginConfigurationModel(tool); - } - this.pluginDescriptions = pluginDescriptions; + this.model = model; addWorkPanel(getWorkPanel()); addOKButton(); @@ -90,7 +87,7 @@ public class PluginInstallerDialog extends DialogComponentProvider { * Returns the details panel. *

* Note: This is primarily for test access - * + * * @return the details panel */ PluginDetailsPanel getDetailsPanel() { @@ -101,20 +98,13 @@ public class PluginInstallerDialog extends DialogComponentProvider { * Returns the filter panel. *

* Note: This is primarily for test access - * + * * @return the filter panel */ GTableFilterPanel getFilterPanel() { return tableFilterPanel; } - /** - * Returns the plugin configuration model. - *

- * Note: This is primarily for test access - * - * @return the plugin configuration model - */ PluginConfigurationModel getModel() { return model; } @@ -127,7 +117,7 @@ public class PluginInstallerDialog extends DialogComponentProvider { JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); - detailsPanel = new PluginDetailsPanel(model); + detailsPanel = new PluginDetailsPanel(tool, model); JPanel pluginTablePanel = createPluginTablePanel(detailsPanel); final JSplitPane splitPane = @@ -174,12 +164,10 @@ public class PluginInstallerDialog extends DialogComponentProvider { table.getColumnModel() .getColumn(PluginInstallerTableModel.NAME_COL) - .setCellRenderer( - new NameCellRenderer()); + .setCellRenderer(new NameCellRenderer()); table.getColumnModel() .getColumn(PluginInstallerTableModel.STATUS_COL) - .setCellRenderer( - new StatusCellRenderer()); + .setCellRenderer(new StatusCellRenderer()); HelpService help = Help.getHelpService(); help.registerHelp(table, new HelpLocation(GenericHelpTopics.TOOL, "PluginDialog")); @@ -237,7 +225,7 @@ public class PluginInstallerDialog extends DialogComponentProvider { } /** - * Renderer for the plugin name column. + * Renderer for the plugin name column. */ private class NameCellRenderer extends GTableCellRenderer { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginManagerComponent.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginManagerComponent.java index 2401078cd5..418f5711a9 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginManagerComponent.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/dialog/PluginManagerComponent.java @@ -20,8 +20,6 @@ import java.util.ArrayList; import java.util.List; import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.swing.event.HyperlinkEvent.EventType; import docking.EmptyBorderToggleButton; @@ -30,24 +28,26 @@ import docking.widgets.checkbox.GCheckBox; import docking.widgets.label.*; import ghidra.framework.plugintool.PluginConfigurationModel; import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.PluginPackage; -import ghidra.framework.plugintool.util.PluginPackageState; +import ghidra.framework.plugintool.util.*; import ghidra.util.HTMLUtilities; +import ghidra.util.exception.AssertException; import ghidra.util.layout.HorizontalLayout; import ghidra.util.layout.VerticalLayout; import resources.ResourceManager; -public class PluginManagerComponent extends JPanel implements ChangeListener, Scrollable { +public class PluginManagerComponent extends JPanel implements Scrollable { private final PluginTool tool; private PluginConfigurationModel model; private List packageComponentList = new ArrayList<>(); - PluginManagerComponent(PluginTool tool) { + PluginManagerComponent(PluginTool tool, PluginConfigurationModel model) { super(new VerticalLayout(2)); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); setBackground(Color.WHITE); this.tool = tool; - model = new PluginConfigurationModel(tool, this); + this.model = model; + model.setChangeCallback(this::updateCheckboxes); + List pluginPackages = model.getPluginPackages(); for (PluginPackage pluginPackage : pluginPackages) { PluginPackageComponent comp = new PluginPackageComponent(pluginPackage); @@ -56,31 +56,66 @@ public class PluginManagerComponent extends JPanel implements ChangeListener, Sc } } - @Override - public void stateChanged(ChangeEvent e) { + private void updateCheckboxes() { for (PluginPackageComponent comp : packageComponentList) { comp.updateCheckBoxState(); } } void managePlugins(PluginPackage pluginPackage) { - PluginInstallerDialog pluginInstallerDialog = - new PluginInstallerDialog("Configure " + pluginPackage.getName() + " Plugins", tool, - model.getPluginDescriptions(pluginPackage)); + List descriptons = model.getPluginDescriptions(pluginPackage); + PluginInstallerDialog pluginInstallerDialog = new PluginInstallerDialog( + "Configure " + pluginPackage.getName() + " Plugins", tool, model, descriptons); tool.showDialog(pluginInstallerDialog); } void manageAllPlugins() { PluginInstallerDialog pluginTableDialog = new PluginInstallerDialog("Configure All Plugins", - tool, model.getAllPluginDescriptions()); + tool, model, model.getAllPluginDescriptions()); tool.showDialog(pluginTableDialog); } + PluginConfigurationModel getModel() { + return model; + } + + int getPackageCount() { + return packageComponentList.size(); + } + + int getPluginCount(PluginPackage pluginPackage) { + return model.getPluginDescriptions(pluginPackage).size(); + } + + void selectPluginPackage(PluginPackage pluginPackage, boolean selected) { + if (selected) { + model.addSupportedPlugins(pluginPackage); + } + else { + model.removeAllPlugins(pluginPackage); + } + + } + + boolean isAddAllCheckBoxEnabled(PluginPackage pluginPackage) { + for (PluginPackageComponent ppc : packageComponentList) { + if (ppc.pluginPackage.equals(pluginPackage)) { + return ppc.checkBox.isEnabled(); + } + } + + throw new AssertException("No checkbox found for " + pluginPackage); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + private class PluginPackageComponent extends JPanel { private final Color BG = Color.white; private final PluginPackage pluginPackage; private final GCheckBox checkBox; - + PluginPackageComponent(PluginPackage pluginPackage) { super(new BorderLayout()); setBackground(BG); @@ -99,28 +134,29 @@ public class PluginManagerComponent extends JPanel implements ChangeListener, Sc private void initizalizeCheckBoxSection() { final JPanel checkboxPanel = new JPanel(new HorizontalLayout(0)); checkboxPanel.setBackground(BG); - - checkBox.addActionListener(e -> checkBoxClicked()); - if (!pluginPackage.isfullyAddable()) { + + checkBox.addActionListener( + e -> selectPluginPackage(pluginPackage, checkBox.isSelected())); + if (model.hasOnlyUnstablePlugins(pluginPackage)) { checkBox.setEnabled(false); } checkBox.setBackground(BG); - + checkboxPanel.add(Box.createHorizontalStrut(10)); checkboxPanel.add(checkBox); checkboxPanel.add(Box.createHorizontalStrut(10)); - + final JLabel iconLabel = new GIconLabel(ResourceManager.getScaledIcon(pluginPackage.getIcon(), 32, 32, 32)); iconLabel.setBackground(BG); - + checkboxPanel.add(iconLabel); checkboxPanel.add(Box.createHorizontalStrut(10)); checkboxPanel.setPreferredSize(new Dimension(84, 70)); - + add(checkboxPanel, BorderLayout.WEST); } - + private void initializeLabelSection() { final JPanel centerPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -128,63 +164,52 @@ public class PluginManagerComponent extends JPanel implements ChangeListener, Sc gbc.weightx = 1.0; centerPanel.setBackground(BG); - + final JPanel labelPanel = new JPanel(new VerticalLayout(3)); labelPanel.setBackground(BG); - + final GLabel nameLabel = new GLabel(pluginPackage.getName()); nameLabel.setFont(nameLabel.getFont().deriveFont(18f)); nameLabel.setForeground(Color.BLACK); labelPanel.add(nameLabel); - + final HyperlinkComponent configureHyperlink = createConfigureHyperlink(); labelPanel.add(configureHyperlink); - + labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 40)); centerPanel.add(labelPanel, gbc); add(centerPanel); } - + private HyperlinkComponent createConfigureHyperlink() { final HyperlinkComponent configureHyperlink = - new HyperlinkComponent(" Configure"); - configureHyperlink.addHyperlinkListener("Configure", e -> { - if (e.getEventType() == EventType.ACTIVATED) { - managePlugins(PluginPackageComponent.this.pluginPackage); - } - }); - configureHyperlink.setBackground(BG); - return configureHyperlink; + new HyperlinkComponent(" Configure"); + configureHyperlink.addHyperlinkListener("Configure", e -> { + if (e.getEventType() == EventType.ACTIVATED) { + managePlugins(PluginPackageComponent.this.pluginPackage); + } + }); + configureHyperlink.setBackground(BG); + return configureHyperlink; } - + private String enchanceDescription(final String text) { return String.format("%s", text); } - + private void initializeDescriptionSection() { final String htmlDescription = enchanceDescription(pluginPackage.getDescription()); - + final JLabel descriptionlabel = new GHtmlLabel(htmlDescription); descriptionlabel.setForeground(Color.GRAY); descriptionlabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); descriptionlabel.setVerticalAlignment(SwingConstants.TOP); descriptionlabel.setToolTipText( HTMLUtilities.toWrappedHTML(pluginPackage.getDescription(), 80)); - + add(descriptionlabel, BorderLayout.EAST); } - protected void checkBoxClicked() { - boolean isSelected = checkBox.isSelected(); - if (isSelected) { - model.addAllPlugins(pluginPackage); - } - else { - model.removeAllPlugins(pluginPackage); - } - - } - void updateCheckBoxState() { checkBox.setSelected( model.getPackageState(pluginPackage) != PluginPackageState.NO_PLUGINS_LOADED); @@ -227,4 +252,5 @@ public class PluginManagerComponent extends JPanel implements ChangeListener, Sc public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 20; } + } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginClassManager.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginClassManager.java index 86d462e901..7e58d16201 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginClassManager.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginClassManager.java @@ -139,9 +139,9 @@ public class PluginClassManager { /** * Used to convert an old style tool XML file by adding in classes in the same packages as - * those that were names specifically in the XML file + * those that were named specifically in the XML file * @param classNames the list of classNames from from the old XML file - * @return + * @return the adjusted class names */ public List fillInPackageClasses(List classNames) { Set packages = new HashSet<>(); @@ -222,30 +222,30 @@ public class PluginClassManager { return list; } - public List getReleasedPluginDescriptions(PluginPackage pluginPackage) { + public List getPluginDescriptions(PluginPackage pluginPackage) { List list = packageMap.get(pluginPackage); List stableList = new ArrayList<>(); for (PluginDescription pluginDescription : list) { - if (pluginDescription.getStatus() == PluginStatus.RELEASED) { - stableList.add(pluginDescription); + if (pluginDescription.getStatus() == PluginStatus.UNSTABLE || + pluginDescription.getStatus() == PluginStatus.HIDDEN) { + continue; } + stableList.add(pluginDescription); } return stableList; } - public List getNonReleasedPluginDescriptions() { + public List getUnstablePluginDescriptions() { List unstablePlugins = new ArrayList<>(); for (PluginDescription pluginDescription : pluginClassMap.values()) { - if (pluginDescription.getStatus() == PluginStatus.HIDDEN || - pluginDescription.getStatus() == PluginStatus.RELEASED) { - continue; + if (pluginDescription.getStatus() == PluginStatus.UNSTABLE) { + unstablePlugins.add(pluginDescription); } - unstablePlugins.add(pluginDescription); } return unstablePlugins; } - public List getAllPluginDescriptions() { + public List getManagedPluginDescriptions() { ArrayList nonHiddenPlugins = new ArrayList<>(); for (PluginDescription pluginDescription : pluginClassMap.values()) { if (pluginDescription.getStatus() == PluginStatus.HIDDEN) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java index df384a2694..a47060f49d 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java @@ -67,7 +67,7 @@ public class PluginDescription implements Comparable { private final List> eventsConsumed; private final List> eventsProduced; - private PluginDescription(Class pluginClass, String pluginPackageName, + PluginDescription(Class pluginClass, String pluginPackageName, String category, String shortDescription, String description, PluginStatus status, boolean isSlowInstallation, List> servicesRequired, List> servicesProvided, List> eventsConsumed, @@ -94,7 +94,7 @@ public class PluginDescription implements Comparable { /** * Returns true if this plugin requires a noticeable amount of time to load when installed. - * @return + * @return true if this plugin requires a noticeable amount of time to load when installed. */ public boolean isSlowInstallation() { return isSlowInstallation; @@ -141,6 +141,7 @@ public class PluginDescription implements Comparable { /** * Return the name of the plugin. + * @return the name of the plugin. */ public String getName() { return name; @@ -186,6 +187,7 @@ public class PluginDescription implements Comparable { /** * Returns the development status of the plugin. + * @return the status. */ public PluginStatus getStatus() { return status; @@ -253,9 +255,9 @@ public class PluginDescription implements Comparable { return name.compareTo(other.name); } - //------------------------------------------------------------------------------------- - // static methods that we don't care about - //------------------------------------------------------------------------------------- +//================================================================================================== +// static methods that will eventually be removed as old client plugins have been updated +//================================================================================================== /** * Constructs a new PluginDescription for the given plugin class. diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginPackage.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginPackage.java index bddf84f82f..a348d0bb4a 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginPackage.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginPackage.java @@ -103,6 +103,15 @@ public abstract class PluginPackage implements ExtensionPoint, Comparable getPluginDescriptions(PluginTool tool, List> plugins) { - // First define the list of plugin descriptions to return. + // First define the list of plugin descriptions to return List retPlugins = new ArrayList<>(); - // Get all plugins that have been loaded. - PluginConfigurationModel model = new PluginConfigurationModel(tool, null); - List allPluginDescriptions = model.getAllPluginDescriptions(); + // Get all plugins that have been loaded + PluginClassManager pluginClassManager = tool.getPluginClassManager(); + List allPluginDescriptions = + pluginClassManager.getManagedPluginDescriptions(); - // For each plugin classes we're searching for, see if an entry exists in the list of all - // loaded plugins. + // see if an entry exists in the list of all loaded plugins for (Class plugin : plugins) { String pluginName = plugin.getSimpleName(); - Optional desc = allPluginDescriptions.stream().filter( - d -> (pluginName.equals(d.getName()))).findAny(); + Optional desc = allPluginDescriptions.stream() + .filter(d -> (pluginName.equals(d.getName()))) + .findAny(); if (desc.isPresent()) { retPlugins.add(desc.get()); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java index 318914c12c..693d9677d7 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/project/tool/GhidraTool.java @@ -32,8 +32,7 @@ import ghidra.framework.main.FrontEndOnly; import ghidra.framework.model.Project; import ghidra.framework.model.ToolTemplate; import ghidra.framework.options.PreferenceState; -import ghidra.framework.plugintool.Plugin; -import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.dialog.*; import ghidra.framework.plugintool.util.*; import ghidra.util.HelpLocation; @@ -256,8 +255,8 @@ public class GhidraTool extends PluginTool { if (option == OptionDialog.YES_OPTION) { List pluginDescriptions = PluginUtils.getPluginDescriptions(this, newPlugins); - PluginInstallerDialog pluginInstaller = - new PluginInstallerDialog("New Plugins Found!", this, pluginDescriptions); + PluginInstallerDialog pluginInstaller = new PluginInstallerDialog("New Plugins Found!", + this, new PluginConfigurationModel(this), pluginDescriptions); showDialog(pluginInstaller); } diff --git a/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescription.java b/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescription.java new file mode 100644 index 0000000000..dd1ad92953 --- /dev/null +++ b/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescription.java @@ -0,0 +1,34 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool.util; + +import java.util.Collections; + +import ghidra.framework.plugintool.Plugin; + +/** + * A basic stub that allows tests to create plugin descriptions + */ +public class StubPluginDescription extends PluginDescription { + + public StubPluginDescription(Class pluginClass, PluginPackage pluginPackage, + String category, String shortDescription, PluginStatus status) { + super(pluginClass, pluginPackage.getName(), category, shortDescription, + "Full description for " + pluginClass.getName(), status, false, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + } + +} diff --git a/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescriptionBuilder.java b/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescriptionBuilder.java new file mode 100644 index 0000000000..41d97b24da --- /dev/null +++ b/Ghidra/Framework/Project/src/test/java/ghidra/framework/plugintool/util/StubPluginDescriptionBuilder.java @@ -0,0 +1,70 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool.util; + +import java.util.Map; + +import generic.test.TestUtils; +import ghidra.framework.plugintool.Plugin; + +/** + * A builder to allow test writers to easily create a {@link StubPluginDescription}. + */ +public class StubPluginDescriptionBuilder { + + private Class clazz; + private PluginPackage pluginPackage; + private PluginStatus status = PluginStatus.RELEASED; + private String category = "Category"; + private String shortDescription = "Short description"; + + public StubPluginDescriptionBuilder(Class clazz, + PluginPackage pluginPackage) { + this.clazz = clazz; + this.pluginPackage = pluginPackage; + } + + public StubPluginDescriptionBuilder status(PluginStatus pluginStatus) { + this.status = pluginStatus; + return this; + } + + public StubPluginDescriptionBuilder category(String pluginCategory) { + this.category = pluginCategory; + return this; + } + + public StubPluginDescriptionBuilder shortDescription(String description) { + this.shortDescription = description; + return this; + } + + public StubPluginDescription build() { + + // as a convenience for test writers, ensure that the given plugin package is registered + // with the system + @SuppressWarnings("unchecked") + Map map = (Map) TestUtils + .getInstanceField("packageMap", PluginPackage.class); + map.put(pluginPackage.getName().toLowerCase(), pluginPackage); + + if (shortDescription == null) { + shortDescription = "Short description for " + clazz.getSimpleName(); + } + + return new StubPluginDescription(clazz, pluginPackage, category, shortDescription, status); + } +}