Merge remote-tracking branch 'origin/GP-4188_ghidra1_ContentToolAssociations--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-01-11 07:54:54 -05:00
commit 9506a1cca2
6 changed files with 100 additions and 170 deletions

View file

@ -1066,9 +1066,6 @@ public class BSimLaunchable implements GhidraLaunchable {
System.err.println("ERROR: " + e.getMessage()); System.err.println("ERROR: " + e.getMessage());
} }
// Allows handling of old content which did not have a content type property
DomainObjectAdapter.setDefaultContentClass(ProgramDB.class);
ApplicationConfiguration config; ApplicationConfiguration config;
switch (type) { switch (type) {
case 2: case 2:

View file

@ -86,9 +86,6 @@ public class GhidraRun implements GhidraLaunchable {
ExtensionUtils.initializeExtensions(); ExtensionUtils.initializeExtensions();
// Allows handling of old content which did not have a content type property
DomainObjectAdapter.setDefaultContentClass(ProgramDB.class);
updateSplashScreenStatusMessage("Checking for previous project..."); updateSplashScreenStatusMessage("Checking for previous project...");
SystemUtilities.runSwingLater(() -> { SystemUtilities.runSwingLater(() -> {
String projectPath = processArguments(args); String projectPath = processArguments(args);

View file

@ -159,9 +159,6 @@ public class GhidraScriptRunner implements GhidraLaunchable {
*/ */
initializeApplication(applicationLayout, logFile, useLog4j); initializeApplication(applicationLayout, logFile, useLog4j);
// Allows handling of old content which did not have a content type property
DomainObjectAdapter.setDefaultContentClass(ProgramDB.class);
initializeScriptPaths(); initializeScriptPaths();
} }

View file

@ -206,9 +206,6 @@ public class HeadlessAnalyzer {
System.setProperty("java.awt.headless", "true"); System.setProperty("java.awt.headless", "true");
System.setProperty(SystemUtilities.HEADLESS_PROPERTY, Boolean.TRUE.toString()); System.setProperty(SystemUtilities.HEADLESS_PROPERTY, Boolean.TRUE.toString());
// Allows handling of old content which did not have a content type property
DomainObjectAdapter.setDefaultContentClass(ProgramDB.class);
// Put analyzer in its default state // Put analyzer in its default state
reset(); reset();

View file

@ -26,6 +26,7 @@ import ghidra.framework.model.*;
import ghidra.framework.store.FileSystem; import ghidra.framework.store.FileSystem;
import ghidra.framework.store.LockException; import ghidra.framework.store.LockException;
import ghidra.util.Lock; import ghidra.util.Lock;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.datastruct.ListenerSet; import ghidra.util.datastruct.ListenerSet;
@ -37,13 +38,12 @@ public abstract class DomainObjectAdapter implements DomainObject {
protected final static String DEFAULT_NAME = "untitled"; protected final static String DEFAULT_NAME = "untitled";
private static Class<?> defaultDomainObjClass; // Domain object implementation mapped to unknown content type
private static HashMap<String, ContentHandler<?>> contentHandlerTypeMap; // maps content-type string to handler private static HashMap<String, ContentHandler<?>> contentHandlerTypeMap; // maps content-type string to handler
private static HashMap<Class<?>, ContentHandler<?>> contentHandlerClassMap; // maps domain object class to handler private static HashMap<Class<?>, ContentHandler<?>> contentHandlerClassMap; // maps domain object class to handler
private static ChangeListener contentHandlerUpdateListener = new ChangeListener() { private static ChangeListener contentHandlerUpdateListener = new ChangeListener() {
@Override @Override
public void stateChanged(ChangeEvent e) { public void stateChanged(ChangeEvent e) {
getContentHandlers(); initContentHandlerMaps();
} }
}; };
@ -383,33 +383,14 @@ public abstract class DomainObjectAdapter implements DomainObject {
} }
/** /**
* Set default content type * Get the {@link ContentHandler} associated with the specified content-type.
*
* @param doClass default domain object implementation
*/
public static synchronized void setDefaultContentClass(Class<?> doClass) {
defaultDomainObjClass = doClass;
if (contentHandlerTypeMap != null) {
if (doClass == null) {
contentHandlerTypeMap.remove(null);
}
else {
ContentHandler<?> ch = contentHandlerClassMap.get(doClass);
if (ch != null) {
contentHandlerTypeMap.put(null, ch);
}
}
}
}
/**
* Get the ContentHandler associated with the specified content-type.
* *
* @param contentType domain object content type * @param contentType domain object content type
* @return content handler * @return content handler
* @throws IOException if no content handler can be found * @throws IOException if no content handler can be found
*/ */
static synchronized ContentHandler<?> getContentHandler(String contentType) throws IOException { public static synchronized ContentHandler<?> getContentHandler(String contentType)
throws IOException {
checkContentHandlerMaps(); checkContentHandlerMaps();
ContentHandler<?> ch = contentHandlerTypeMap.get(contentType); ContentHandler<?> ch = contentHandlerTypeMap.get(contentType);
if (ch == null) { if (ch == null) {
@ -419,20 +400,40 @@ public abstract class DomainObjectAdapter implements DomainObject {
} }
/** /**
* Get the ContentHandler associated with the specified domain object * Get the {@link ContentHandler} associated with the specified domain object class
*
* @param dobjClass domain object class
* @return content handler
* @throws IOException if no content handler can be found
*/
public static synchronized ContentHandler<?> getContentHandler(
Class<? extends DomainObject> dobjClass) throws IOException {
checkContentHandlerMaps();
ContentHandler<?> ch = contentHandlerClassMap.get(dobjClass);
if (ch == null) {
throw new IOException("Content handler not found for " + dobjClass.getName());
}
return ch;
}
/**
* Get the {@link ContentHandler} associated with the specified domain object
* *
* @param dobj domain object * @param dobj domain object
* @return content handler * @return content handler
* @throws IOException if no content handler can be found * @throws IOException if no content handler can be found
*/ */
public static synchronized ContentHandler<?> getContentHandler(DomainObject dobj) public static ContentHandler<?> getContentHandler(DomainObject dobj) throws IOException {
throws IOException { return getContentHandler(dobj.getClass());
}
/**
* Get all {@link ContentHandler}s
* @return collection of content handlers
*/
public static Set<ContentHandler<?>> getContentHandlers() {
checkContentHandlerMaps(); checkContentHandlerMaps();
ContentHandler<?> ch = contentHandlerClassMap.get(dobj.getClass()); return new HashSet<>(contentHandlerTypeMap.values());
if (ch == null) {
throw new IOException("Content handler not found for " + dobj.getClass().getName());
}
return ch;
} }
private static void checkContentHandlerMaps() { private static void checkContentHandlerMaps() {
@ -440,23 +441,34 @@ public abstract class DomainObjectAdapter implements DomainObject {
return; return;
} }
getContentHandlers(); initContentHandlerMaps();
ClassSearcher.addChangeListener(contentHandlerUpdateListener); ClassSearcher.addChangeListener(contentHandlerUpdateListener);
} }
private synchronized static void getContentHandlers() { private synchronized static void initContentHandlerMaps() {
contentHandlerClassMap = new HashMap<Class<?>, ContentHandler<?>>(); HashMap<Class<?>, ContentHandler<?>> classMap = new HashMap<>();
contentHandlerTypeMap = new HashMap<String, ContentHandler<?>>(); HashMap<String, ContentHandler<?>> typeMap = new HashMap<>();
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
List<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class); List<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler<?> ch : handlers) { for (ContentHandler<?> ch : handlers) {
contentHandlerTypeMap.put(ch.getContentType(), ch); String contentType = ch.getContentType();
if (typeMap.put(contentType, ch) != null) {
Msg.error(DomainObjectAdapter.class,
"Multiple content handlers discovered for content type: " + contentType);
}
if (!(ch instanceof LinkHandler<?>)) { if (!(ch instanceof LinkHandler<?>)) {
contentHandlerClassMap.put(ch.getDomainObjectClass(), ch); Class<? extends DomainObjectAdapter> contentClass = ch.getDomainObjectClass();
if (classMap.put(contentClass, ch) != null) {
Msg.error(DomainObjectAdapter.class,
"Multiple content handlers discovered for content class: " +
contentClass.getSimpleName());
}
} }
} }
setDefaultContentClass(defaultDomainObjClass);
contentHandlerClassMap = classMap;
contentHandlerTypeMap = typeMap;
} }
@Override @Override

View file

@ -35,7 +35,6 @@ import ghidra.framework.preferences.Preferences;
import ghidra.framework.protocol.ghidra.GetUrlContentTypeTask; import ghidra.framework.protocol.ghidra.GetUrlContentTypeTask;
import ghidra.framework.protocol.ghidra.GhidraURL; import ghidra.framework.protocol.ghidra.GhidraURL;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.filechooser.GhidraFileChooserModel; import ghidra.util.filechooser.GhidraFileChooserModel;
import ghidra.util.filechooser.GhidraFileFilter; import ghidra.util.filechooser.GhidraFileFilter;
import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskLauncher;
@ -53,7 +52,6 @@ class ToolServicesImpl implements ToolServices {
private ToolChest toolChest; private ToolChest toolChest;
private ToolManagerImpl toolManager; private ToolManagerImpl toolManager;
private ToolChestChangeListener toolChestChangeListener; private ToolChestChangeListener toolChestChangeListener;
private Set<ContentHandler<?>> contentHandlers;
ToolServicesImpl(ToolChest toolChest, ToolManagerImpl toolManager) { ToolServicesImpl(ToolChest toolChest, ToolManagerImpl toolManager) {
this.toolChest = toolChest; this.toolChest = toolChest;
@ -320,8 +318,11 @@ class ToolServicesImpl implements ToolServices {
Set<ToolAssociationInfo> set = new HashSet<>(); Set<ToolAssociationInfo> set = new HashSet<>();
// get all known content types // get all known content types
Set<ContentHandler<?>> handlers = getContentHandlers(); Set<? extends ContentHandler<?>> handlers = DomainObjectAdapter.getContentHandlers();
for (ContentHandler<?> contentHandler : handlers) { for (ContentHandler<?> contentHandler : handlers) {
if (contentHandler instanceof LinkHandler) {
continue;
}
set.add(createToolAssociationInfo(contentHandler)); set.add(createToolAssociationInfo(contentHandler));
} }
@ -351,6 +352,22 @@ class ToolServicesImpl implements ToolServices {
@Override @Override
public ToolTemplate getDefaultToolTemplate(String contentType) { public ToolTemplate getDefaultToolTemplate(String contentType) {
try {
ContentHandler<?> contentHandler = DomainObjectAdapter.getContentHandler(contentType);
if (contentHandler instanceof LinkHandler) {
Class<? extends DomainObjectAdapter> domainObjectClass =
contentHandler.getDomainObjectClass();
contentHandler = DomainObjectAdapter.getContentHandler(domainObjectClass);
contentType = contentHandler.getContentType();
}
}
catch (IOException e) {
// Failed to identify content handler
Msg.error(this, e.getMessage());
return null;
}
String toolName = String toolName =
Preferences.getProperty(getToolAssociationPreferenceKey(contentType), null, true); Preferences.getProperty(getToolAssociationPreferenceKey(contentType), null, true);
if (toolName == null) { if (toolName == null) {
@ -379,28 +396,31 @@ class ToolServicesImpl implements ToolServices {
} }
// //
// Next, look through for all compatible content handlers find tools for them // Next, check content handler for its default tool name
// //
Set<ContentHandler<?>> compatibleHandlers = getCompatibleContentHandlers(domainClass); try {
for (ContentHandler<?> handler : compatibleHandlers) { ContentHandler<?> handler = DomainObjectAdapter.getContentHandler(domainClass);
String defaultToolName = handler.getDefaultToolName(); String defaultToolName = handler.getDefaultToolName();
if (nameToTemplateMap.get(defaultToolName) != null) { if (nameToTemplateMap.get(defaultToolName) == null) {
continue; // already have tool in the map by this name; prefer that tool ToolTemplate toolChestTemplate = findToolChestToolTemplate(defaultToolName);
} if (toolChestTemplate != null) {
// found the tool in the tool chest--use that one
ToolTemplate toolChestTemplate = findToolChestToolTemplate(defaultToolName); nameToTemplateMap.put(toolChestTemplate.getName(), toolChestTemplate);
if (toolChestTemplate != null) { }
// found the tool in the tool chest--use that one else {
nameToTemplateMap.put(toolChestTemplate.getName(), toolChestTemplate); // see if there is a default tool
continue; GhidraToolTemplate defaultToolTemplate =
} findDefaultToolTemplate(defaultToolName);
if (defaultToolTemplate != null) {
// see if there is a default tool nameToTemplateMap.put(defaultToolTemplate.getName(), defaultToolTemplate);
GhidraToolTemplate defaultToolTemplate = findDefaultToolTemplate(defaultToolName); }
if (defaultToolTemplate != null) { }
nameToTemplateMap.put(defaultToolTemplate.getName(), defaultToolTemplate);
} }
} }
catch (IOException e) {
// Failed to identify content handler
Msg.error(this, e.getMessage());
}
// //
// Finally, see if any of the default tools can handle this type and include any that // Finally, see if any of the default tools can handle this type and include any that
@ -424,70 +444,23 @@ class ToolServicesImpl implements ToolServices {
return new HashSet<>(nameToTemplateMap.values()); return new HashSet<>(nameToTemplateMap.values());
} }
private Set<ContentHandler<?>> getCompatibleContentHandlers(
Class<? extends DomainObject> domainClass) {
Set<ContentHandler<?>> set = new HashSet<>();
Set<ContentHandler<?>> handlers = getContentHandlers();
for (ContentHandler<?> contentHandler : handlers) {
Class<? extends DomainObject> handlerDomainClass =
contentHandler.getDomainObjectClass();
if (handlerDomainClass == domainClass) {
set.add(contentHandler);
}
}
return set;
}
private String getToolAssociationPreferenceKey(String contentType) { private String getToolAssociationPreferenceKey(String contentType) {
return TOOL_ASSOCIATION_PREFERENCE + SEPARATOR + contentType; return TOOL_ASSOCIATION_PREFERENCE + SEPARATOR + contentType;
} }
private String getDefaultToolAssociation(String contentType) { private String getDefaultToolAssociation(String contentType) {
Set<ContentHandler<?>> handlers = getContentHandlers();
for (ContentHandler<?> contentHandler : handlers) { try {
String type = contentHandler.getContentType(); ContentHandler<?> contentHandler = DomainObjectAdapter.getContentHandler(contentType);
if (type.equals(contentType)) { return contentHandler.getDefaultToolName();
return contentHandler.getDefaultToolName(); }
} catch (IOException e) {
// Failed to identify content handler
Msg.error(this, e.getMessage());
} }
return null; return null;
} }
private Set<ContentHandler<?>> getContentHandlers() {
if (contentHandlers != null) {
return contentHandlers;
}
contentHandlers = new HashSet<>();
@SuppressWarnings("rawtypes")
List<ContentHandler> instances = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler<?> contentHandler : instances) {
if (contentHandler instanceof FolderLinkContentHandler) {
continue; // ignore folder link handler
}
// a bit of validation
String contentType = contentHandler.getContentType();
if (contentType == null) {
Msg.error(DomainObjectAdapter.class, "ContentHandler<?> " +
contentHandler.getClass().getName() + " does not specify a content type");
continue;
}
String toolName = contentHandler.getDefaultToolName();
if (toolName == null) {
Msg.error(DomainObjectAdapter.class, "ContentHandler<?> " +
contentHandler.getClass().getName() + " does not specify a default tool");
continue;
}
contentHandlers.add(contentHandler);
}
return contentHandlers;
}
private GhidraToolTemplate findToolChestToolTemplate(String toolName) { private GhidraToolTemplate findToolChestToolTemplate(String toolName) {
if (toolName != null) { if (toolName != null) {
return (GhidraToolTemplate) toolChest.getToolTemplate(toolName); return (GhidraToolTemplate) toolChest.getToolTemplate(toolName);
@ -509,54 +482,11 @@ class ToolServicesImpl implements ToolServices {
return null; return null;
} }
/**
* Get all running tools that have the same tool chest tool name as this one.
*
* @param tool the tool for comparison.
*
* @return array of tools that are running and named the same as this one.
*/
private PluginTool[] getSameNamedRunningTools(PluginTool tool) {
String toolName = tool.getToolName();
PluginTool[] tools = toolManager.getRunningTools();
List<PluginTool> toolList = new ArrayList<>(tools.length);
for (PluginTool element : tools) {
if (toolName.equals(element.getToolName())) {
toolList.add(element);
}
}
return toolList.toArray(new PluginTool[toolList.size()]);
}
@Override @Override
public PluginTool[] getRunningTools() { public PluginTool[] getRunningTools() {
return toolManager.getRunningTools(); return toolManager.getRunningTools();
} }
/**
* Search the array of tools for one using the given domainFile.
*
* @param tools array of tools to search
* @param domainFile domain file to find user of
*
* @return first tool found to be using the domainFile
*/
private PluginTool findToolUsingFile(PluginTool[] tools, DomainFile domainFile) {
PluginTool matchingTool = null;
for (int toolNum = 0; (toolNum < tools.length) && (matchingTool == null); toolNum++) {
PluginTool pTool = tools[toolNum];
// Is this tool the same as the type we are in.
DomainFile[] df = pTool.getDomainFiles();
for (DomainFile element : df) {
if (domainFile.equals(element)) {
matchingTool = tools[toolNum];
break;
}
}
}
return matchingTool;
}
@Override @Override
public boolean canAutoSave(PluginTool tool) { public boolean canAutoSave(PluginTool tool) {
return toolManager.canAutoSave(tool); return toolManager.canAutoSave(tool);