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

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