GP-1764 - Plugins - tweaked help

This commit is contained in:
dragonmacher 2022-01-18 13:50:30 -05:00
parent 82c42e648c
commit 924bf7656e
30 changed files with 1200 additions and 496 deletions

View File

@ -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<String, List<PluginDescription>> pluginMap = new HashMap<>();
private Map<String, List<Analyzer>> analyzerMap = new HashMap<>();
@Override
public void run() throws Exception {
PluginConfigurationModel model = new PluginConfigurationModel(state.getTool());
List<PluginDescription> descriptions = model.getAllPluginDescriptions();
for (PluginDescription pluginDescription : descriptions) {
String moduleName = pluginDescription.getModuleName();
if (moduleName == null) {
moduleName = "[ No Module ]";
}
addPlugin(moduleName, pluginDescription);
}
List<Analyzer> instances = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : instances) {
Class<? extends Analyzer> 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<String> set = new HashSet<>(pluginMap.keySet());
set.addAll(analyzerMap.keySet());
List<String> list = new ArrayList<>(set);
Collections.sort(list);
System.out.println("Modules:");
for (String module : list) {
System.out.println("\t" + module);
List<PluginDescription> 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<Analyzer> 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<Analyzer> 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<PluginDescription> list = pluginMap.get(moduleName);
if (list == null) {
list = new ArrayList<>();
pluginMap.put(moduleName, list);
}
list.add(pluginDescription);
}
}

View File

@ -28,8 +28,12 @@
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <I>Configure Tool</I> 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 <B>Configure</B> 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.
<P>Clicking on the <B>Configure</B> link will bring up a dialog for adding individual
plugins.</P>
<BLOCKQUOTE>

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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<String> 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<Plugin> loadedPlugins = new ArrayList<>();
private Set<String> addedPlugins = new HashSet<>();
private Set<String> removedPlugins = new HashSet<>();
@Override
public void addPlugins(List<String> 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<Plugin> array) {
for (Plugin plugin : array) {
loadedPlugins.remove(plugin);
removedPlugins.remove(plugin.getClass().getSimpleName());
}
}
@Override
public List<Plugin> getManagedPlugins() {
return loadedPlugins;
}
Set<String> getAddedPlugins() {
return addedPlugins;
}
}
private class StubPluginPackagingProvider implements PluginPackagingProvider {
private List<PluginPackage> packages = new ArrayList<>();
private List<PluginDescription> descriptions = new ArrayList<>();
private List<PluginDescription> unstableDescriptions = new ArrayList<>();
@Override
public List<PluginPackage> getPluginPackages() {
return packages;
}
void addPackage(PluginPackage p) {
packages.add(p);
}
@Override
public List<PluginDescription> getPluginDescriptions() {
return descriptions;
}
void addDescription(PluginDescription pd) {
descriptions.add(pd);
}
@Override
public PluginDescription getPluginDescription(String pluginClassName) {
List<PluginDescription> results = descriptions.stream()
.filter(pd -> pd.getPluginClass().getName().equals(pluginClassName))
.collect(Collectors.toList());
assertEquals(1, results.size());
return results.get(0);
}
@Override
public List<PluginDescription> getPluginDescriptions(PluginPackage pluginPackage) {
List<PluginDescription> 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<PluginDescription> 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
*/
}

View File

@ -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();
}
}

View File

@ -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<FidDbViewerProvider> 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");
}

View File

@ -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> T askChoice(String title, String message, List<T> choices, T defaultValue) {
AskDialog<T> dialog =

View File

@ -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.");
}
}

View File

@ -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;
}

View File

@ -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<PluginPackage> getPluginPackages() {
return pluginClassManager.getPluginPackages();
}
@Override
public List<PluginDescription> getPluginDescriptions() {
return pluginClassManager.getManagedPluginDescriptions();
}
@Override
public PluginDescription getPluginDescription(String pluginClassName) {
return pluginClassManager.getPluginDescription(pluginClassName);
}
@Override
public List<PluginDescription> getPluginDescriptions(PluginPackage pluginPackage) {
return pluginClassManager.getPluginDescriptions(pluginPackage);
}
@Override
public PluginPackage getUnstablePluginPackage() {
return UNSTABLE_PACKAGE;
}
@Override
public List<PluginDescription> getUnstablePluginDescriptions() {
return pluginClassManager.getUnstablePluginDescriptions();
}
}

View File

@ -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<Plugin> getManagedPlugins() {
return tool.getManagedPlugins();
}
@Override
public void addPlugins(List<String> pluginClassNames) throws PluginException {
tool.addPlugins(pluginClassNames);
}
@Override
public void removePlugins(List<Plugin> plugins) {
tool.removePlugins(plugins);
}
}

View File

@ -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<PluginDescription, Plugin> loadedPluginMap = new HashMap<>();
private Set<PluginDescription> pluginsWithDependenciesSet = new HashSet<>();
private List<PluginDescription> 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<PluginPackage> getPluginPackages() {
List<PluginPackage> pluginPackages = pluginClassManager.getPluginPackages();
List<PluginPackage> pluginPackages = pluginPackagingProvider.getPluginPackages();
List<PluginPackage> 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<Plugin> list = tool.getManagedPlugins();
List<Plugin> 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<Plugin> 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<PluginDescription> pluginDescriptions = getPluginDescriptions(pluginPackage);
public void addSupportedPlugins(PluginPackage pluginPackage) {
PluginStatus activationLevel = pluginPackage.getActivationLevel();
List<PluginDescription> pluginDescriptions = getPluginDescriptions(pluginPackage);
List<String> 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<PluginDescription> 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<DockingActionIf> 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<PluginDescription> 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<PluginDescription> 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<PluginDescription> getAllPluginDescriptions() {
return pluginClassManager.getAllPluginDescriptions();
return pluginPackagingProvider.getPluginDescriptions();
}
}

View File

@ -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<Plugin> 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<String> pluginClassNames) throws PluginException;
/**
* Removes the given plugins from the system
* @param plugins the plugins
*/
public void removePlugins(List<Plugin> plugins);
}

View File

@ -86,9 +86,9 @@ class PluginManager {
addPlugins(new Plugin[] { plugin });
}
void addPlugins(String[] classNames) throws PluginException {
void addPlugins(List<String> classNames) throws PluginException {
PluginException pe = null;
List<Plugin> list = new ArrayList<>(classNames.length);
List<Plugin> list = new ArrayList<>(classNames.size());
List<String> 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<Plugin> 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++) {

View File

@ -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<PluginPackage> getPluginPackages();
/**
* Returns all loaded plugin descriptions
* @return the descriptions
*/
public List<PluginDescription> 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<PluginDescription> 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<PluginDescription> getUnstablePluginDescriptions();
}

View File

@ -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}).
*
*
* <p>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<String> 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<Plugin> 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 {
* <br>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 {
* <br>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

View File

@ -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;
}
}

View File

@ -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("</TD>");
Set<DockingActionIf> actions = model.getActionsForPlugin(pluginDescription);
if (actions.size() == 0) {
Set<DockingActionIf> actions = Collections.emptySet();
if (model.isLoaded(pluginDescription)) {
actions =
KeyBindingUtils.getKeyBindingActionsForOwner(tool, pluginDescription.getName());
}
if (actions.isEmpty()) {
buffer.append("<TD VALIGN=\"TOP\">");
insertHTMLLine(buffer, "No actions for plugin", noValueAttrSet);
buffer.append("</TD>");

View File

@ -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<PluginDescription> 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.
* <p>
* 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.
* <p>
* Note: This is primarily for test access
*
*
* @return the filter panel
*/
GTableFilterPanel<PluginDescription> getFilterPanel() {
return tableFilterPanel;
}
/**
* Returns the plugin configuration model.
* <p>
* 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 {

View File

@ -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<PluginPackageComponent> 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<PluginPackage> 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<PluginDescription> 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("<html> <a href=\"Configure\">Configure</a>");
configureHyperlink.addHyperlinkListener("Configure", e -> {
if (e.getEventType() == EventType.ACTIVATED) {
managePlugins(PluginPackageComponent.this.pluginPackage);
}
});
configureHyperlink.setBackground(BG);
return configureHyperlink;
new HyperlinkComponent("<html> <a href=\"Configure\">Configure</a>");
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("<html><body style='width: 300px'>%s</body></html>", 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;
}
}

View File

@ -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<String> fillInPackageClasses(List<String> classNames) {
Set<PluginPackage> packages = new HashSet<>();
@ -222,30 +222,30 @@ public class PluginClassManager {
return list;
}
public List<PluginDescription> getReleasedPluginDescriptions(PluginPackage pluginPackage) {
public List<PluginDescription> getPluginDescriptions(PluginPackage pluginPackage) {
List<PluginDescription> list = packageMap.get(pluginPackage);
List<PluginDescription> 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<PluginDescription> getNonReleasedPluginDescriptions() {
public List<PluginDescription> getUnstablePluginDescriptions() {
List<PluginDescription> 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<PluginDescription> getAllPluginDescriptions() {
public List<PluginDescription> getManagedPluginDescriptions() {
ArrayList<PluginDescription> nonHiddenPlugins = new ArrayList<>();
for (PluginDescription pluginDescription : pluginClassMap.values()) {
if (pluginDescription.getStatus() == PluginStatus.HIDDEN) {

View File

@ -67,7 +67,7 @@ public class PluginDescription implements Comparable<PluginDescription> {
private final List<Class<? extends PluginEvent>> eventsConsumed;
private final List<Class<? extends PluginEvent>> eventsProduced;
private PluginDescription(Class<? extends Plugin> pluginClass, String pluginPackageName,
PluginDescription(Class<? extends Plugin> pluginClass, String pluginPackageName,
String category, String shortDescription, String description, PluginStatus status,
boolean isSlowInstallation, List<Class<?>> servicesRequired,
List<Class<?>> servicesProvided, List<Class<? extends PluginEvent>> eventsConsumed,
@ -94,7 +94,7 @@ public class PluginDescription implements Comparable<PluginDescription> {
/**
* 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<PluginDescription> {
/**
* 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<PluginDescription> {
/**
* Returns the development status of the plugin.
* @return the status.
*/
public PluginStatus getStatus() {
return status;
@ -253,9 +255,9 @@ public class PluginDescription implements Comparable<PluginDescription> {
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.

View File

@ -103,6 +103,15 @@ public abstract class PluginPackage implements ExtensionPoint, Comparable<Plugin
return description;
}
/**
* The minimum level required to activate plugins when the entire package is activated by the
* user.
* @return the minimum level
*/
public PluginStatus getActivationLevel() {
return PluginStatus.RELEASED;
}
@Override
public int compareTo(PluginPackage other) {
if (priority == other.priority) {
@ -111,7 +120,8 @@ public abstract class PluginPackage implements ExtensionPoint, Comparable<Plugin
return priority - other.priority;
}
public boolean isfullyAddable() {
return true;
@Override
public String toString() {
return name;
}
}

View File

@ -5,9 +5,9 @@
* 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.

View File

@ -52,20 +52,21 @@ public class PluginUtils {
public static List<PluginDescription> getPluginDescriptions(PluginTool tool,
List<Class<?>> plugins) {
// First define the list of plugin descriptions to return.
// First define the list of plugin descriptions to return
List<PluginDescription> retPlugins = new ArrayList<>();
// Get all plugins that have been loaded.
PluginConfigurationModel model = new PluginConfigurationModel(tool, null);
List<PluginDescription> allPluginDescriptions = model.getAllPluginDescriptions();
// Get all plugins that have been loaded
PluginClassManager pluginClassManager = tool.getPluginClassManager();
List<PluginDescription> 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<PluginDescription> desc = allPluginDescriptions.stream().filter(
d -> (pluginName.equals(d.getName()))).findAny();
Optional<PluginDescription> desc = allPluginDescriptions.stream()
.filter(d -> (pluginName.equals(d.getName())))
.findAny();
if (desc.isPresent()) {
retPlugins.add(desc.get());
}

View File

@ -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<PluginDescription> 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);
}

View File

@ -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<? extends Plugin> 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());
}
}

View File

@ -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<? extends Plugin> clazz;
private PluginPackage pluginPackage;
private PluginStatus status = PluginStatus.RELEASED;
private String category = "Category";
private String shortDescription = "Short description";
public StubPluginDescriptionBuilder(Class<? extends Plugin> 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<String, PluginPackage> map = (Map<String, PluginPackage>) 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);
}
}