Refactored plugin configuration methods

This commit is contained in:
dragonmacher 2023-06-26 12:34:53 -04:00 committed by ghidragon
parent 5719632656
commit f5f71426d9
17 changed files with 274 additions and 176 deletions

View file

@ -30,7 +30,7 @@ import ghidra.framework.model.Project;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginClassManager;
import ghidra.framework.plugintool.util.PluginsConfiguration;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@ -413,8 +413,8 @@ public class AnalyzeAllOpenProgramsTaskTest extends AbstractGhidraHeadedIntegrat
}
@Override
public PluginClassManager getPluginClassManager() {
return null;
public PluginsConfiguration createPluginsConfigurations() {
return null; // prevent slow class loading
}
@Override

View file

@ -0,0 +1,29 @@
/* ###
* 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.main;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.util.PluginsConfiguration;
/**
* A configuration that only includes {@link ApplicationLevelPlugin} plugins.
*/
class ApplicationLevelPluginsConfiguration extends PluginsConfiguration {
@Override
protected boolean accepts(Class<? extends Plugin> c) {
return ApplicationLevelPlugin.class.isAssignableFrom(c);
}
}

View file

@ -131,7 +131,6 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener {
private WindowListener windowListener;
private DockingAction configureToolAction;
private PluginClassManager pluginClassManager;
/**
* Construct a new Ghidra Project Window.
@ -190,11 +189,8 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener {
}
@Override
public PluginClassManager getPluginClassManager() {
if (pluginClassManager == null) {
pluginClassManager = new PluginClassManager(ApplicationLevelPlugin.class, null);
}
return pluginClassManager;
protected PluginsConfiguration createPluginsConfigurations() {
return new ApplicationLevelPluginsConfiguration();
}
public void selectFiles(Set<DomainFile> files) {

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,9 +16,9 @@
package ghidra.framework.main;
/**
* Marker interface for plugins that only get constructed programatically for specific purposes.
* Marker interface for plugins that only get constructed programmatically for specific purposes.
* Plugins that implement this interface should never be added via the config GUIs.
*/
public interface ProgramaticUseOnly {
// marker interface
}

View file

@ -20,13 +20,13 @@ import java.util.List;
import ghidra.framework.plugintool.util.*;
/**
* The default plugin package provider that uses the {@link PluginClassManager} to supply packages
* The default plugin package provider that uses the {@link PluginsConfiguration} to supply packages
*/
public class DeafultPluginPackagingProvider implements PluginPackagingProvider {
private PluginClassManager pluginClassManager;
private PluginsConfiguration pluginClassManager;
DeafultPluginPackagingProvider(PluginClassManager pluginClassManager) {
DeafultPluginPackagingProvider(PluginsConfiguration pluginClassManager) {
this.pluginClassManager = pluginClassManager;
}

View file

@ -17,13 +17,13 @@ package ghidra.framework.plugintool;
import ghidra.framework.main.AppInfo;
import ghidra.framework.model.Project;
import ghidra.framework.plugintool.util.PluginClassManager;
import ghidra.framework.plugintool.util.PluginsConfiguration;
/**
* PluginTool that is used by the Merge process to resolve conflicts
* when a file is being checked into a server repository. This tool
* is modal while it is visible.
*
*
*/
public class ModalPluginTool extends PluginTool {
@ -44,7 +44,7 @@ public class ModalPluginTool extends PluginTool {
}
@Override
public PluginClassManager getPluginClassManager() {
return null;
public PluginsConfiguration createPluginsConfigurations() {
return null; // no need to load plugins
}
}

View file

@ -34,7 +34,7 @@ public class PluginConfigurationModel {
public PluginConfigurationModel(PluginTool tool) {
this(new DefaultPluginInstaller(tool),
new DeafultPluginPackagingProvider(tool.getPluginClassManager()));
new DeafultPluginPackagingProvider(tool.getPluginsConfiguration()));
}
public PluginConfigurationModel(PluginInstaller pluginInstaller,

View file

@ -23,6 +23,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom.Element;
import ghidra.framework.main.UtilityPluginPackage;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.SaveState;
@ -33,15 +34,40 @@ import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.MultipleCauses;
class PluginManager {
static final Logger log = LogManager.getLogger(PluginManager.class);
private static final Logger log = LogManager.getLogger(PluginManager.class);
private PluginsConfiguration pluginsConfiguration;
private List<Plugin> pluginList = new ArrayList<>();
private PluginTool tool;
private ServiceManager serviceMgr;
PluginManager(PluginTool tool, ServiceManager serviceMgr) {
PluginManager(PluginTool tool, ServiceManager serviceMgr,
PluginsConfiguration pluginsConfiguration) {
this.tool = tool;
this.serviceMgr = serviceMgr;
this.pluginsConfiguration = pluginsConfiguration;
}
void installUtilityPlugins() throws PluginException {
PluginPackage utilityPackage = PluginPackage.getPluginPackage(UtilityPluginPackage.NAME);
List<PluginDescription> descriptions =
pluginsConfiguration.getPluginDescriptions(utilityPackage);
if (descriptions == null) {
return;
}
Set<String> classNames = new HashSet<>();
for (PluginDescription description : descriptions) {
String pluginClass = description.getPluginClass().getName();
classNames.add(pluginClass);
}
addPlugins(classNames);
}
PluginsConfiguration getPluginsConfiguration() {
return pluginsConfiguration;
}
boolean acceptData(DomainFile[] data) {
@ -55,9 +81,9 @@ class PluginManager {
}
/**
* Identify plugin which will accept specified URL. If no plugin accepts URL it will be
* rejected and false returned. If a plugin can accept the specified URL it will attempt to
* process and return true if successful.
* Identify plugin which will accept specified URL. If no plugin accepts URL it will be
* rejected and false returned. If a plugin can accept the specified URL it will attempt to
* process and return true if successful.
* The user may be prompted if connecting to the URL requires user authentication.
* @param url read-only resource URL
* @return true if URL accepted and processed else false
@ -72,7 +98,7 @@ class PluginManager {
return false;
}
public void dispose() {
void dispose() {
for (Iterator<Plugin> it = pluginList.iterator(); it.hasNext();) {
Plugin plugin = it.next();
plugin.cleanup();
@ -255,15 +281,14 @@ class PluginManager {
}
void saveToXml(Element root, boolean includeConfigState) {
PluginClassManager pluginClassManager = tool.getPluginClassManager();
pluginClassManager.addXmlElementsForPlugins(root, pluginList);
pluginsConfiguration.savePluginsToXml(root, pluginList);
if (!includeConfigState) {
return;
}
SaveState saveState = new SaveState("PLUGIN_STATE");
Iterator<Plugin> it = pluginList.iterator();
while (it.hasNext()) {
Plugin p = it.next();
@ -279,8 +304,8 @@ class PluginManager {
void restorePluginsFromXml(Element root) throws PluginException {
boolean isOld = isOldToolConfig(root);
Collection<String> classNames =
isOld ? getPluginClassNamesFromOldXml(root) : getPluginClassNamesToLoad(root);
Collection<String> classNames = isOld ? getPluginClassNamesFromOldXml(root)
: pluginsConfiguration.getPluginClassNames(root);
Map<String, SaveState> map = isOld ? getPluginSavedStates(root, "PLUGIN")
: getPluginSavedStates(root, "PLUGIN_STATE");
@ -324,19 +349,14 @@ class PluginManager {
String className = elem.getAttributeValue("CLASS");
classNames.add(className);
}
PluginClassManager pluginClassManager = tool.getPluginClassManager();
return pluginClassManager.fillInPackageClasses(classNames);
return pluginsConfiguration.getPluginNamesByCurrentPackage(classNames);
}
private boolean isOldToolConfig(Element root) {
return root.getChild("PLUGIN") != null;
}
private Set<String> getPluginClassNamesToLoad(Element root) {
PluginClassManager pluginClassManager = tool.getPluginClassManager();
return pluginClassManager.getPluginClasses(root);
}
/**
* Restore the data state from the given XML element.
* @param root XML element containing plugins' data state
@ -465,8 +485,9 @@ class PluginManager {
// TODO: this following loop is searching for any non-loaded Plugin that implements
// the required service class interface.
// This doesn't seem exactly right as a Service implementation shouldn't
// be automagically pulled in and instantiated UNLESS it was specified as the "defaultProvider",
// which we've already checked for in the previous PluginUtils.getDefaultProviderForServiceClass().
// be automagically pulled in and instantiated UNLESS it was specified as the
// "defaultProvider", which we've already checked for in the previous
// PluginUtils.getDefaultProviderForServiceClass().
// TODO: this also should be filtered by the PluginClassManager so we don't
// pull in classes that have been excluded.
for (Class<? extends Plugin> pc : ClassSearcher.getClasses(Plugin.class)) {

View file

@ -45,7 +45,8 @@ import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.cmd.Command;
import ghidra.framework.main.*;
import ghidra.framework.main.AppInfo;
import ghidra.framework.main.UserAgreementDialog;
import ghidra.framework.model.*;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.dialog.ExtensionTableProvider;
@ -175,7 +176,7 @@ public abstract class PluginTool extends AbstractDockingTool {
eventMgr = new EventManager(this);
serviceMgr = new ServiceManager();
installServices();
pluginMgr = new PluginManager(this, serviceMgr);
pluginMgr = new PluginManager(this, serviceMgr, createPluginsConfigurations());
dialogMgr = new DialogManager(this);
initActions();
initOptions();
@ -192,7 +193,13 @@ public abstract class PluginTool extends AbstractDockingTool {
// non-public constructor for stub subclasses
}
public abstract PluginClassManager getPluginClassManager();
protected PluginsConfiguration createPluginsConfigurations() {
return new DefaultPluginsConfiguration();
}
protected PluginsConfiguration getPluginsConfiguration() {
return pluginMgr.getPluginsConfiguration();
}
/**
* This method exists here, as opposed to inline in the constructor, so that subclasses can
@ -233,21 +240,15 @@ public abstract class PluginTool extends AbstractDockingTool {
*/
protected void installUtilityPlugins() {
PluginClassManager classManager = getPluginClassManager();
PluginPackage utilityPackage = PluginPackage.getPluginPackage(UtilityPluginPackage.NAME);
List<PluginDescription> descriptions = classManager.getPluginDescriptions(utilityPackage);
Set<String> classNames = new HashSet<>();
if (descriptions == null) {
return;
}
for (PluginDescription description : descriptions) {
String pluginClass = description.getPluginClass().getName();
classNames.add(pluginClass);
}
try {
addPlugins(classNames);
checkedRunSwingNow(() -> {
try {
pluginMgr.installUtilityPlugins();
}
finally {
setConfigChanged(true);
}
}, PluginException.class);
}
catch (PluginException e) {
Msg.showError(this, null, "Error Adding Utility Plugins",

View file

@ -23,12 +23,12 @@ import docking.action.*;
import docking.tool.ToolConstants;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.framework.plugintool.util.PluginClassManager;
import ghidra.framework.plugintool.util.PluginsConfiguration;
import ghidra.util.HelpLocation;
public class StandAlonePluginTool extends PluginTool {
private PluginClassManager pluginClassManager;
private PluginsConfiguration pluginClassManager;
private DockingAction configureToolAction;
private final GenericStandAloneApplication app;
private final String name;
@ -39,14 +39,6 @@ public class StandAlonePluginTool extends PluginTool {
this.name = name;
}
@Override
public PluginClassManager getPluginClassManager() {
if (pluginClassManager == null) {
pluginClassManager = new PluginClassManager(Plugin.class, null);
}
return pluginClassManager;
}
@Override
public void addExitAction() {
DockingAction exitAction = new DockingAction("Exit", ToolConstants.TOOL_OWNER) {

View file

@ -0,0 +1,28 @@
/* ###
* 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 ghidra.framework.plugintool.Plugin;
/**
* A configuration that includes all plugins on the classpath.
*/
public class DefaultPluginsConfiguration extends PluginsConfiguration {
@Override
protected boolean accepts(Class<? extends Plugin> pluginClass) {
return true;
}
}

View file

@ -33,48 +33,6 @@ import ghidra.util.exception.AssertException;
*/
public class PluginUtils {
/**
* Finds all {@link PluginDescription} objects that match a given set of plugin classes. This
* effectively tells the caller which of the given plugins have been loaded by the class loader.
* <p>
* eg: If the list of plugin classes contains the class "FooPlugin.class", this method
* will search the {@link PluginConfigurationModel} for any plugin with the name "FooPlugin" and
* return its {@link PluginDescription}.
* <p>
* Note that this method does not take path/package information into account when finding
* plugins; in the example above, if there is more than one plugin with the name "FooPlugin",
* only one will be found (the one found is not guaranteed to be the first).
*
* @param tool the current tool
* @param plugins the list of plugin classes to search for
* @return list of plugin descriptions
*/
public static List<PluginDescription> getPluginDescriptions(PluginTool tool,
List<Class<?>> plugins) {
// First define the list of plugin descriptions to return
List<PluginDescription> retPlugins = new ArrayList<>();
// Get all plugins that have been loaded
PluginClassManager pluginClassManager = tool.getPluginClassManager();
List<PluginDescription> allPluginDescriptions =
pluginClassManager.getManagedPluginDescriptions();
// 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();
if (desc.isPresent()) {
retPlugins.add(desc.get());
}
}
return retPlugins;
}
/**
* Finds all plugin classes loaded from a given set of extensions.
*

View file

@ -15,6 +15,8 @@
*/
package ghidra.framework.plugintool.util;
import static java.util.function.Predicate.*;
import java.util.*;
import java.util.function.Predicate;
@ -25,30 +27,33 @@ import ghidra.framework.plugintool.Plugin;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
public class PluginClassManager {
/**
* This class maintains a collection of all plugin classes that are acceptable for a given tool
* type. Simple applications with only one plugin type can use the
* {@link DefaultPluginsConfiguration}. More complex tools can support a subset of the available
* plugins. Those tools should create custom subclasses for each tool type, that filter out plugins
* that are not appropriate for that tool type.
*/
public abstract class PluginsConfiguration {
private Map<PluginPackage, List<PluginDescription>> packageMap = new HashMap<>();
private Map<PluginPackage, List<PluginDescription>> descriptionsByPackage = new HashMap<>();
private Map<String, PluginDescription> descriptionsByName = new HashMap<>();
private Map<String, PluginDescription> pluginClassMap = new HashMap<>();
public PluginClassManager(Class<?> filterClass, Class<?> exclusionClass) {
populatePluginDescriptionMaps(filterClass, exclusionClass);
protected PluginsConfiguration() {
populatePluginDescriptionMaps();
}
public PluginDescription getPluginDescription(String className) {
return pluginClassMap.get(className);
protected abstract boolean accepts(Class<? extends Plugin> pluginClass);
private Predicate<Class<? extends Plugin>> createFilter() {
Predicate<Class<? extends Plugin>> ignore = ProgramaticUseOnly.class::isAssignableFrom;
return not(ignore).and(c -> accepts(c));
}
private void populatePluginDescriptionMaps(Class<?> localFilterClass,
Class<?> localExclusionClass) {
private void populatePluginDescriptionMaps() {
Predicate<Class<? extends Plugin>> myClassFilter =
c -> (localFilterClass == null || localFilterClass.isAssignableFrom(c)) &&
(localExclusionClass == null || !localExclusionClass.isAssignableFrom(c)) &&
!ProgramaticUseOnly.class.isAssignableFrom(c);
List<Class<? extends Plugin>> classes =
ClassSearcher.getClasses(Plugin.class, myClassFilter);
Predicate<Class<? extends Plugin>> classFilter = createFilter();
List<Class<? extends Plugin>> classes = ClassSearcher.getClasses(Plugin.class, classFilter);
for (Class<? extends Plugin> pluginClass : classes) {
if (!PluginUtils.isValidPluginClass(pluginClass)) {
@ -57,16 +62,21 @@ public class PluginClassManager {
}
PluginDescription pd = PluginDescription.getPluginDescription(pluginClass);
pluginClassMap.put(pluginClass.getName(), pd);
descriptionsByName.put(pluginClass.getName(), pd);
PluginPackage pluginPackage = pd.getPluginPackage();
List<PluginDescription> list =
packageMap.computeIfAbsent(pluginPackage, (k) -> new ArrayList<>());
descriptionsByPackage.computeIfAbsent(pluginPackage, (k) -> new ArrayList<>());
list.add(pd);
}
}
public void addXmlElementsForPlugins(Element root, List<Plugin> plugins) {
public PluginDescription getPluginDescription(String className) {
return descriptionsByName.get(className);
}
public void savePluginsToXml(Element root, List<Plugin> plugins) {
Map<PluginPackage, List<Plugin>> pluginPackageMap = buildPluginPackageMap(plugins);
for (PluginPackage pluginPackage : pluginPackageMap.keySet()) {
root.addContent(getPackageElement(pluginPackage, pluginPackageMap.get(pluginPackage)));
@ -76,7 +86,7 @@ public class PluginClassManager {
private Element getPackageElement(PluginPackage pluginPackage, List<Plugin> pluginList) {
Element packageElement = new Element("PACKAGE");
packageElement.setAttribute("NAME", pluginPackage.getName());
List<PluginDescription> pluginDescriptions = packageMap.get(pluginPackage);
List<PluginDescription> pluginDescriptions = descriptionsByPackage.get(pluginPackage);
Set<String> includedPluginClasses = new HashSet<>();
for (Plugin plugin : pluginList) {
@ -120,7 +130,8 @@ public class PluginClassManager {
private Map<PluginPackage, List<Plugin>> buildPluginPackageMap(List<Plugin> plugins) {
Map<PluginPackage, List<Plugin>> pluginPackageMap = new HashMap<>();
for (Plugin plugin : plugins) {
PluginDescription pluginDescription = pluginClassMap.get(plugin.getClass().getName());
PluginDescription pluginDescription =
descriptionsByName.get(plugin.getClass().getName());
if (pluginDescription == null) {
continue;
}
@ -138,41 +149,42 @@ public class PluginClassManager {
}
/**
* Used to convert an old style tool XML file by adding in classes in the same packages as
* those that were named specifically in the XML file
* Used to convert an old style tool XML file by mapping the given class names to plugin
* packages and then adding <b>all</b> plugins in that package. This has the effect of pulling
* in more plugin classes than were originally specified in the tool xml.
*
* @param classNames the list of classNames from from the old XML file
* @return the adjusted class names
* @return the adjusted set of plugin class names
*/
public Set<String> fillInPackageClasses(List<String> classNames) {
public Set<String> getPluginNamesByCurrentPackage(List<String> classNames) {
Set<PluginPackage> packages = new HashSet<>();
Set<String> adjustedClassNames = new HashSet<>();
for (String className : classNames) {
PluginDescription pluginDescription = pluginClassMap.get(className);
if (pluginDescription != null) {
if (pluginDescription.getStatus() == PluginStatus.RELEASED) {
packages.add(pluginDescription.getPluginPackage());
}
else {
adjustedClassNames.add(className);
}
PluginDescription pd = descriptionsByName.get(className);
if (pd == null) {
continue; // plugin no longer in tool
}
if (pd.getStatus() == PluginStatus.RELEASED) {
packages.add(pd.getPluginPackage());
}
else {
adjustedClassNames.add(className);
}
}
for (PluginPackage pluginPackage : packages) {
List<PluginDescription> list = packageMap.get(pluginPackage);
for (PluginDescription pluginDescription : list) {
if (pluginDescription.getStatus() != PluginStatus.RELEASED) {
continue;
}
String name = pluginDescription.getPluginClass().getName();
adjustedClassNames.add(name);
List<PluginDescription> packageDescriptions = descriptionsByPackage.get(pluginPackage);
for (PluginDescription pd : packageDescriptions) {
adjustedClassNames.add(pd.getPluginClass().getName());
}
}
return adjustedClassNames;
}
public Set<String> getPluginClasses(Element element) {
public Set<String> getPluginClassNames(Element element) {
Set<String> classNames = new HashSet<>();
List<?> children = element.getChildren("PACKAGE");
@ -206,7 +218,8 @@ public class PluginClassManager {
}
PluginPackage pluginPackage = PluginPackage.getPluginPackage(packageName);
List<PluginDescription> pluginDescriptionList = packageMap.get(pluginPackage);
List<PluginDescription> pluginDescriptionList =
descriptionsByPackage.get(pluginPackage);
if (pluginDescriptionList == null) {
continue;
}
@ -236,13 +249,13 @@ public class PluginClassManager {
}
public List<PluginPackage> getPluginPackages() {
List<PluginPackage> list = new ArrayList<>(packageMap.keySet());
List<PluginPackage> list = new ArrayList<>(descriptionsByPackage.keySet());
Collections.sort(list);
return list;
}
public List<PluginDescription> getPluginDescriptions(PluginPackage pluginPackage) {
List<PluginDescription> list = packageMap.get(pluginPackage);
List<PluginDescription> list = descriptionsByPackage.get(pluginPackage);
List<PluginDescription> stableList = new ArrayList<>();
for (PluginDescription pluginDescription : list) {
if (pluginDescription.getStatus() == PluginStatus.UNSTABLE ||
@ -256,7 +269,7 @@ public class PluginClassManager {
public List<PluginDescription> getUnstablePluginDescriptions() {
List<PluginDescription> unstablePlugins = new ArrayList<>();
for (PluginDescription pluginDescription : pluginClassMap.values()) {
for (PluginDescription pluginDescription : descriptionsByName.values()) {
if (pluginDescription.getStatus() == PluginStatus.UNSTABLE) {
unstablePlugins.add(pluginDescription);
}
@ -266,7 +279,7 @@ public class PluginClassManager {
public List<PluginDescription> getManagedPluginDescriptions() {
ArrayList<PluginDescription> nonHiddenPlugins = new ArrayList<>();
for (PluginDescription pluginDescription : pluginClassMap.values()) {
for (PluginDescription pluginDescription : descriptionsByName.values()) {
if (pluginDescription.getStatus() == PluginStatus.HIDDEN) {
continue;
}

View file

@ -0,0 +1,32 @@
/* ###
* 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.project.tool;
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.util.PluginsConfiguration;
/**
* A configuration that allows all general plugins and application plugins. Plugins that may only
* exist at the application level are filtered out.
*/
class GhidraPluginsConfiguration extends PluginsConfiguration {
@Override
protected boolean accepts(Class<? extends Plugin> c) {
return !(ApplicationLevelOnlyPlugin.class.isAssignableFrom(c));
}
}

View file

@ -28,11 +28,11 @@ import docking.action.MenuData;
import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import ghidra.app.util.FileOpenDropHandler;
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
import ghidra.framework.model.Project;
import ghidra.framework.model.ToolTemplate;
import ghidra.framework.options.PreferenceState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.PluginConfigurationModel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.dialog.*;
import ghidra.framework.plugintool.util.*;
import ghidra.util.HelpLocation;
@ -55,9 +55,6 @@ public class GhidraTool extends PluginTool {
public static boolean autoSave = true;
private FileOpenDropHandler fileOpenDropHandler;
private PluginClassManager pluginClassManager;
private DockingAction configureToolAction;
/**
@ -99,12 +96,8 @@ public class GhidraTool extends PluginTool {
}
@Override
public PluginClassManager getPluginClassManager() {
if (pluginClassManager == null) {
pluginClassManager =
new PluginClassManager(Plugin.class, ApplicationLevelOnlyPlugin.class);
}
return pluginClassManager;
protected PluginsConfiguration createPluginsConfigurations() {
return new GhidraPluginsConfiguration();
}
@Override
@ -254,8 +247,7 @@ public class GhidraTool extends PluginTool {
int option = OptionDialog.showYesNoDialog(getActiveWindow(), "New Plugins Found!",
"New extension plugins detected. Would you like to configure them?");
if (option == OptionDialog.YES_OPTION) {
List<PluginDescription> pluginDescriptions =
PluginUtils.getPluginDescriptions(this, newPlugins);
List<PluginDescription> pluginDescriptions = getPluginDescriptions(this, newPlugins);
PluginInstallerDialog pluginInstaller = new PluginInstallerDialog("New Plugins Found!",
this, new PluginConfigurationModel(this), pluginDescriptions);
showDialog(pluginInstaller);
@ -265,6 +257,43 @@ public class GhidraTool extends PluginTool {
addInstalledExtensions(newExtensions);
}
/**
* Finds all {@link PluginDescription} objects that match a given set of plugin classes. This
* effectively tells the caller which of the given plugins have been loaded by the class loader.
* <p>
* Note that this method does not take path/package information into account when finding
* plugins; in the example above, if there is more than one plugin with the name "FooPlugin",
* only one will be found (the one found is not guaranteed to be the first).
*
* @param tool the current tool
* @param plugins the list of plugin classes to search for
* @return list of plugin descriptions
*/
private List<PluginDescription> getPluginDescriptions(PluginTool tool, List<Class<?>> plugins) {
// First define the list of plugin descriptions to return
List<PluginDescription> retPlugins = new ArrayList<>();
// Get all plugins that have been loaded
PluginsConfiguration pluginClassManager = getPluginsConfiguration();
List<PluginDescription> allPluginDescriptions =
pluginClassManager.getManagedPluginDescriptions();
// 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();
if (desc.isPresent()) {
retPlugins.add(desc.get());
}
}
return retPlugins;
}
/**
* Removes any extensions in the tool preferences that are no longer installed.
*/

View file

@ -15,10 +15,10 @@
*/
package ghidra.framework.plugintool;
import ghidra.framework.plugintool.util.PluginClassManager;
import ghidra.framework.plugintool.util.PluginsConfiguration;
/**
* A dummy version of {@link PluginTool} that tests can use when they need an instance of
* A dummy version of {@link PluginTool} that tests can use when they need an instance of
* the PluginTool, but do not wish to use a real version
*/
public class DummyPluginTool extends PluginTool {
@ -29,7 +29,7 @@ public class DummyPluginTool extends PluginTool {
}
@Override
public PluginClassManager getPluginClassManager() {
protected PluginsConfiguration createPluginsConfigurations() {
return null;
}

View file

@ -401,7 +401,7 @@ public class DummyTool extends PluginTool {
@Override
public void removeContextListener(DockingContextListener listener) {
//do nothing
//do nothing
}
@Override
@ -416,16 +416,16 @@ public class DummyTool extends PluginTool {
@Override
public void addServiceListener(ServiceListener listener) {
//do nothing
//do nothing
}
@Override
public void removeServiceListener(ServiceListener listener) {
//do nothing
//do nothing
}
@Override
public PluginClassManager getPluginClassManager() {
protected PluginsConfiguration createPluginsConfigurations() {
return null;
}