Merge remote-tracking branch 'origin/GT-3350_ryanmkurtz_ExtensionPoints'

This commit is contained in:
Ryan Kurtz 2019-12-03 10:13:23 -05:00
commit 36a628ae97
52 changed files with 341 additions and 169 deletions

View file

@ -17,7 +17,8 @@ package ghidra.app.util.disassemble;
import java.awt.Color;
import java.math.BigInteger;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.FieldLocation;
@ -43,7 +44,7 @@ public class ExternalDisassemblyFieldFactory extends FieldFactory {
availableDisassemblers = new ArrayList<>();
// find the available external disassemblers
Set<ExternalDisassembler> extDisassemblers =
List<ExternalDisassembler> extDisassemblers =
ClassSearcher.getInstances(ExternalDisassembler.class);
for (ExternalDisassembler disassember : extDisassemblers) {

View file

@ -42,7 +42,7 @@ public class DumpGhidraCapabilitiesScript extends GhidraScript {
addPlugin(moduleName, pluginDescription);
}
Set<Analyzer> instances = ClassSearcher.getInstances(Analyzer.class);
List<Analyzer> instances = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : instances) {
Class<? extends Analyzer> clazz = analyzer.getClass();

View file

@ -68,7 +68,7 @@ public class SummarizeAnalyzers extends GhidraScript {
Options options = currentProgram.getOptions(Program.ANALYSIS_PROPERTIES);
Set<Class<? extends Analyzer>> classes = ClassSearcher.getClasses(Analyzer.class);
List<Class<? extends Analyzer>> classes = ClassSearcher.getClasses(Analyzer.class);
for (Class<? extends Analyzer> element : classes) {
Analyzer analyzer;
try {

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.label;
import java.util.Set;
import java.util.List;
import ghidra.app.util.demangler.*;
import ghidra.framework.cmd.BackgroundCommand;
@ -33,7 +33,7 @@ public class DemanglerCmd extends BackgroundCommand {
private String mangled;
private String result;
private DemangledObject demangledObject;
private static Set<Demangler> demanglers;
private static List<Demangler> demanglers;
private DemanglerOptions options;
public DemanglerCmd(Address addr, String mangled) {
@ -138,7 +138,7 @@ public class DemanglerCmd extends BackgroundCommand {
return demangledObject;
}
private static Set<Demangler> getDemanglers() {
private static List<Demangler> getDemanglers() {
if (demanglers == null) {
demanglers = ClassSearcher.getInstances(Demangler.class);
}

View file

@ -158,7 +158,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
taskArray = new AnalysisTaskList[] { byteTasks, instructionTasks, functionTasks,
functionModifierChangedTasks, functionSignatureChangedTasks, dataTasks };
Set<Analyzer> analyzers = ClassSearcher.getInstances(Analyzer.class);
List<Analyzer> analyzers = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : analyzers) {
if (!analyzer.canAnalyze(program)) {
continue;

View file

@ -19,15 +19,13 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.util.MemoryByteIterator;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* This class is used for the computation of various basic checksums.
*/
@ExtensionPoint.Exclude(reason = "Subclasses will get picked up by extension points.")
public class BasicChecksumAlgorithm extends ChecksumAlgorithm {
public abstract class BasicChecksumAlgorithm extends ChecksumAlgorithm {
/**
* The byte sizes that are supported by the basic checksum algorithm.

View file

@ -17,7 +17,8 @@ package ghidra.app.plugin.core.checksums;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
@ -63,7 +64,7 @@ public class ComputeChecksumsProvider extends ComponentProviderAdapter {
super(plugin.getTool(), "Checksum Generator", plugin.getName(), ProgramContextAction.class);
setHelpLocation(new HelpLocation("ComputeChecksumsPlugin", "Generate_Checksum_Help"));
Set<ChecksumAlgorithm> algorithms = ClassSearcher.getInstances(ChecksumAlgorithm.class);
List<ChecksumAlgorithm> algorithms = ClassSearcher.getInstances(ChecksumAlgorithm.class);
checksums.addAll(algorithms);
this.plugin = plugin;

View file

@ -22,7 +22,6 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.util.MemoryByteIterator;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -30,8 +29,7 @@ import ghidra.util.task.TaskMonitor;
* This class is used for the computation of various digest checksums that are provided
* by java. These checksums do not have options associated with them.
*/
@ExtensionPoint.Exclude(reason = "Subclasses will get picked up by extension points.")
public class DigestChecksumAlgorithm extends ChecksumAlgorithm {
public abstract class DigestChecksumAlgorithm extends ChecksumAlgorithm {
MessageDigest digester;

View file

@ -112,7 +112,7 @@ public class ExportToHeaderAction extends DockingAction {
*/
private void exportToC(GTree gTree, DataTypeManager programDataTypeMgr) {
Set<Class<? extends AnnotationHandler>> classes =
List<Class<? extends AnnotationHandler>> classes =
ClassSearcher.getClasses(AnnotationHandler.class);
List<AnnotationHandler> list = new ArrayList<>();

View file

@ -154,7 +154,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
@SuppressWarnings({ "rawtypes", "unchecked" })
private Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> createAllPossibleCodeComparisonPanels() {
Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> instances = new HashSet<>();
Set<Class<? extends CodeComparisonPanel>> classes =
List<Class<? extends CodeComparisonPanel>> classes =
ClassSearcher.getClasses(CodeComparisonPanel.class);
for (Class<? extends CodeComparisonPanel> panelClass : classes) {
try {

View file

@ -284,7 +284,7 @@ public final class ReferenceUtils {
Accumulator<LocationReference> accumulator, Program program, DataType dataType,
String fieldName, TaskMonitor monitor) throws CancelledException {
Set<DataTypeReferenceFinder> finders =
List<DataTypeReferenceFinder> finders =
ClassSearcher.getInstances(DataTypeReferenceFinder.class);
Consumer<DataTypeReference> callback = ref -> {

View file

@ -54,7 +54,7 @@ import resources.ResourceManager;
public class OverviewColorPlugin extends ProgramPlugin {
public static final String HELP_TOPIC = "OverviewPlugin";
private static final String ACTIVE_SERVICES = "ActiveServices";
private Set<OverviewColorService> allServices;
private List<OverviewColorService> allServices;
private Map<OverviewColorService, OverviewColorComponent> activeServices =
new LinkedHashMap<>(); // maintain the left to right order of the active overview bars.
private CodeViewerService codeViewerService;

View file

@ -15,18 +15,6 @@
*/
package ghidra.app.plugin.core.validator;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.*;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import java.lang.reflect.Constructor;
import java.util.*;
@ -35,6 +23,17 @@ import docking.action.DockingAction;
import docking.action.MenuData;
import docking.tool.ToolConstants;
import docking.widgets.conditiontestpanel.ConditionTester;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
/**
* Display a pop-up dialog to run PostAnalysisValidator tests on the Program
@ -117,7 +116,7 @@ public class ValidateProgramPlugin extends Plugin {
private List<ConditionTester> getConditionTests(Program program) {
List<ConditionTester> list = new ArrayList<ConditionTester>();
Set<Class<? extends PostAnalysisValidator>> validatorClasses =
List<Class<? extends PostAnalysisValidator>> validatorClasses =
ClassSearcher.getClasses(PostAnalysisValidator.class);
for (Class<? extends PostAnalysisValidator> validatorClass : validatorClasses) {
try {

View file

@ -16,7 +16,6 @@
package ghidra.app.util.demangler;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -39,7 +38,7 @@ public class DemanglerUtil {
* @return the demangled object or null
*/
public static DemangledObject demangle(String mangled) {
Set<Demangler> demanglers = getDemanglers();
List<Demangler> demanglers = getDemanglers();
for (Demangler demangler : demanglers) {
try {
// not sure if we should be doing all symbols, but this is what it used to do
@ -65,7 +64,7 @@ public class DemanglerUtil {
* @return the demangled object or null
*/
public static DemangledObject demangle(Program program, String mangled) {
Set<Demangler> demanglers = getDemanglers();
List<Demangler> demanglers = getDemanglers();
for (Demangler demangler : demanglers) {
try {
if (!demangler.canDemangle(program)) {
@ -91,7 +90,7 @@ public class DemanglerUtil {
*
* @return a list of all demanglers
*/
private static Set<Demangler> getDemanglers() {
private static List<Demangler> getDemanglers() {
return ClassSearcher.getInstances(Demangler.class);
}

View file

@ -52,7 +52,7 @@ public class Annotation {
Map<String, AnnotatedStringHandler> map = new HashMap<>();
// find all instances of AnnotatedString
Set<AnnotatedStringHandler> instances =
List<AnnotatedStringHandler> instances =
ClassSearcher.getInstances(AnnotatedStringHandler.class);
for (AnnotatedStringHandler instance : instances) {

View file

@ -15,7 +15,8 @@
*/
package ghidra.app.util.viewer.format;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jdom.Element;
@ -139,7 +140,7 @@ public class FormatManager implements OptionsChangeListener {
private void getFactorys() {
ClassFilter filter = new ClassExclusionFilter(DummyFieldFactory.class,
SpacerFieldFactory.class, SubDataFieldFactory.class);
Set<FieldFactory> instances = ClassSearcher.getInstances(FieldFactory.class, filter);
List<FieldFactory> instances = ClassSearcher.getInstances(FieldFactory.class, filter);
List<FieldFactory> list = new ArrayList<>();
for (FieldFactory fieldFactory : instances) {
if (fieldFactory instanceof SpacerFieldFactory) {

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.
@ -16,6 +15,10 @@
*/
package ghidra.app.util.viewer.util;
import java.awt.event.MouseEvent;
import java.util.*;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.*;
import ghidra.app.util.viewer.field.*;
@ -23,11 +26,6 @@ import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.util.ProgramLocation;
import ghidra.util.classfinder.ClassSearcher;
import java.awt.event.MouseEvent;
import java.util.*;
import docking.widgets.fieldpanel.support.FieldLocation;
/**
* Helper class to navigate to an address when user double clicks in a
* Field. This class will find {@link FieldMouseHandlerExtension}s by using the {@link ClassSearcher}.
@ -84,7 +82,7 @@ public class FieldNavigator implements ButtonPressedListener, FieldMouseHandlerS
new HashMap<Class<?>, List<FieldMouseHandler>>();
// find all instances of AnnotatedString
Set<FieldMouseHandlerExtension> instances =
List<FieldMouseHandlerExtension> instances =
ClassSearcher.getInstances(FieldMouseHandlerExtension.class);
for (FieldMouseHandlerExtension fieldMouseHandler : instances) {
addHandler(map, fieldMouseHandler);

View file

@ -16,7 +16,7 @@
package ghidra.framework;
import java.io.File;
import java.util.Set;
import java.util.List;
import generic.jar.ResourceFile;
import ghidra.GhidraClassLoader;
@ -111,7 +111,7 @@ public class HeadlessGhidraApplicationConfiguration extends ApplicationConfigura
}
private void performModuleInitialization() {
Set<ModuleInitializer> instances = ClassSearcher.getInstances(ModuleInitializer.class);
List<ModuleInitializer> instances = ClassSearcher.getInstances(ModuleInitializer.class);
for (ModuleInitializer initializer : instances) {
monitor.setMessage("Initializing " + initializer.getName() + "...");
initializer.run();

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.
@ -16,12 +15,13 @@
*/
package ghidra.framework.analysis;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.services.Analyzer;
import ghidra.program.model.listing.Program;
import ghidra.util.classfinder.ClassSearcher;
import java.util.*;
public class AnalysisRecipeBuilder {
private static List<Class<? extends Analyzer>> classes;
@ -39,7 +39,7 @@ public class AnalysisRecipeBuilder {
private static AnalysisRecipe buildDefaultRecipe(Program program) {
List<Analyzer> analyzerList = new ArrayList<Analyzer>();
Set<Analyzer> anayzers = ClassSearcher.getInstances(Analyzer.class);
List<Analyzer> anayzers = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : anayzers) {
if (analyzer.canAnalyze(program)) {
analyzerList.add(analyzer);

View file

@ -27,11 +27,11 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.classfinder.ExtensionPointProperties;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ExtensionPoint.Exclude(reason = "requires explicit instantiation to wrap a script")
@ExtensionPointProperties(exclude = true) // exclude class from extension point discovery because it has to be directly instantiated in order to wrap the supplied script
public class GhidraScriptAnalyzerAdapter extends AbstractAnalyzer {
private ResourceFile scriptFile;

View file

@ -18,7 +18,6 @@ package ghidra.framework.plugintool.dialog;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import org.junit.*;
@ -88,7 +87,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest
count++;
}
Set<Class<? extends FrontEndable>> classes = ClassSearcher.getClasses(FrontEndable.class);
List<Class<? extends FrontEndable>> classes = ClassSearcher.getClasses(FrontEndable.class);
assertEquals(count, classes.size());
}

View file

@ -17,6 +17,7 @@ package ghidra.app.plugin.core.byteviewer;
import java.awt.event.*;
import java.math.BigInteger;
import java.util.List;
import java.util.Set;
import javax.swing.*;
@ -696,7 +697,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
@Override
protected Set<DataFormatModel> getDataFormatModels() {
Set<DataFormatModel> dataFormatModels = super.getDataFormatModels();
Set<ProgramDataFormatModel> instances =
List<ProgramDataFormatModel> instances =
ClassSearcher.getInstances(ProgramDataFormatModel.class);
dataFormatModels.addAll(instances);
return dataFormatModels;

View file

@ -15,13 +15,13 @@
*/
package ghidra.file.analyzers;
import ghidra.util.classfinder.ClassSearcher;
import java.util.List;
import java.util.Set;
import ghidra.util.classfinder.ClassSearcher;
public final class FileFormatAnalyzerFactory {
public final static Set<FileFormatAnalyzer> getAnalyzers() {
public final static List<FileFormatAnalyzer> getAnalyzers() {
return ClassSearcher.getInstances(FileFormatAnalyzer.class);
}
}

View file

@ -15,15 +15,15 @@
*/
package ghidra.file.crypto;
import java.io.IOException;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.util.Set;
/**
* Not used by any known code, not tested.
*/
@ -34,7 +34,7 @@ public final class DecryptorFactory {
ByteProvider provider, TaskMonitor monitor) throws IOException, CryptoException,
CancelledException {
Set<Decryptor> instances = ClassSearcher.getInstances(Decryptor.class);
List<Decryptor> instances = ClassSearcher.getInstances(Decryptor.class);
for (Decryptor decryptor : instances) {
if (monitor.isCancelled()) {
throw new CancelledException();

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.functiongraph;
import java.util.Set;
import java.util.List;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
@ -27,8 +27,8 @@ import ghidra.util.classfinder.ClassSearcher;
public class DiscoverableFGLayoutFinder implements FGLayoutFinder {
@Override
public Set<FGLayoutProvider> findLayouts() {
Set<FGLayoutProvider> instances = ClassSearcher.getInstances(FGLayoutProvider.class);
public List<FGLayoutProvider> findLayouts() {
List<FGLayoutProvider> instances = ClassSearcher.getInstances(FGLayoutProvider.class);
return instances;
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.functiongraph;
import java.util.Set;
import java.util.List;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
@ -24,5 +24,5 @@ import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
* An interface that provides {@link FGLayout}s
*/
public interface FGLayoutFinder {
public Set<FGLayoutProvider> findLayouts();
public List<FGLayoutProvider> findLayouts();
}

View file

@ -135,7 +135,7 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
private List<FGLayoutProvider> loadLayoutProviders() {
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
Set<FGLayoutProvider> instances = layoutFinder.findLayouts();
List<FGLayoutProvider> instances = layoutFinder.findLayouts();
if (instances.isEmpty()) {
throw new AssertException("Could not find any layout providers. You project may not " +
"be configured properly.");

View file

@ -59,7 +59,7 @@ public class FidService {
this.digestFactory = new FNV1a64MessageDigestFactory();
this.skippers = new HashMap<>();
Set<Class<? extends InstructionSkipper>> classes =
List<Class<? extends InstructionSkipper>> classes =
ClassSearcher.getClasses(InstructionSkipper.class);
for (Class<? extends InstructionSkipper> clazz : classes) {
try {

View file

@ -15,6 +15,9 @@
*/
package ghidra.feature.vt;
import java.io.IOException;
import java.util.*;
import ghidra.app.script.GhidraScript;
import ghidra.feature.vt.api.db.VTSessionDB;
import ghidra.feature.vt.api.main.*;
@ -26,9 +29,6 @@ import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import java.io.IOException;
import java.util.*;
public abstract class GhidraVersionTrackingScript extends GhidraScript {
protected VTSession vtSession;
protected Program sourceProgram;
@ -162,7 +162,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
public List<String> getProgramCorrelators() {
List<String> correlators = new ArrayList<String>();
Set<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
List<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) {
correlators.add(vtProgramCorrelatorFactory.getName());
}
@ -221,7 +221,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
//==================================================================================================
private VTProgramCorrelatorFactory getCorrelatorFactory(String name) {
Set<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
List<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) {
if (vtProgramCorrelatorFactory.getName().equals(name)) {
return vtProgramCorrelatorFactory;
@ -230,7 +230,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
return null;
}
private static Set<VTProgramCorrelatorFactory> getVTProgramCorrelatorFactory() {
private static List<VTProgramCorrelatorFactory> getVTProgramCorrelatorFactory() {
return ClassSearcher.getInstances(VTProgramCorrelatorFactory.class);
}
}

View file

@ -40,7 +40,7 @@ public abstract class Stringable implements ExtensionPoint, DisplayStringProvide
private static void initializeNameMap() {
shortNameToClassnameMap = new HashMap<String, Class<? extends Stringable>>();
Set<Class<? extends Stringable>> classes = ClassSearcher.getClasses(Stringable.class);
List<Class<? extends Stringable>> classes = ClassSearcher.getClasses(Stringable.class);
for (Class<? extends Stringable> clazz : classes) {
String name = getShortNameFieldValue(clazz);
shortNameToClassnameMap.put(name, clazz);

View file

@ -62,7 +62,7 @@ public class AddressCorrelatorManager {
private List<AddressCorrelator> initializeAddressCorrelators() {
Set<DiscoverableAddressCorrelator> instances =
List<DiscoverableAddressCorrelator> instances =
ClassSearcher.getInstances(DiscoverableAddressCorrelator.class);
List<AddressCorrelator> addressCorrelatorList = new ArrayList<AddressCorrelator>(instances);

View file

@ -17,7 +17,8 @@ package ghidra.feature.vt.gui.provider.matchtable;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
@ -98,7 +99,7 @@ public class AlgorithmFilter extends CheckBoxBasedAncillaryFilter<VTMatch> {
list.add(ManualMatchProgramCorrelator.NAME);
list.add(ImpliedMatchProgramCorrelator.NAME);
Set<VTAbstractProgramCorrelatorFactory> instances =
List<VTAbstractProgramCorrelatorFactory> instances =
ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class);
for (VTAbstractProgramCorrelatorFactory factory : instances) {
list.add(factory.getName());

View file

@ -15,13 +15,6 @@
*/
package ghidra.feature.vt.gui.wizard;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.validator.VTPreconditionValidator;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -33,6 +26,12 @@ import javax.swing.*;
import docking.widgets.conditiontestpanel.*;
import docking.wizard.*;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.validator.VTPreconditionValidator;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
public class PreconditionsPanel extends AbstractMageJPanel<VTWizardStateKey> implements Scrollable {
private static final Dimension DEFAULT_SIZE = new Dimension(650, 480);
@ -148,7 +147,7 @@ public class PreconditionsPanel extends AbstractMageJPanel<VTWizardStateKey> imp
Program destinationProgram, VTSession existingResults) throws SecurityException {
List<ConditionTester> list = new ArrayList<ConditionTester>();
Set<Class<? extends VTPreconditionValidator>> vtValidatorClasses =
List<Class<? extends VTPreconditionValidator>> vtValidatorClasses =
ClassSearcher.getClasses(VTPreconditionValidator.class);
for (Class<? extends VTPreconditionValidator> validatorClass : vtValidatorClasses) {
try {

View file

@ -60,13 +60,12 @@ public class VTProgramTableCorrelatorModel extends AbstractGTableModel<VTProgram
}
private static List<VTProgramCorrelatorFactory> generateList() {
Set<VTAbstractProgramCorrelatorFactory> instances =
List<VTAbstractProgramCorrelatorFactory> instances =
ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class);
List<VTProgramCorrelatorFactory> list =
new ArrayList<>(instances);
List<VTProgramCorrelatorFactory> list = new ArrayList<>(instances);
Collections.sort(list, comparator);
Collections.sort(instances, comparator);
return list;
}

View file

@ -208,7 +208,7 @@ public class DiscoverableTableUtils {
Set<TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE>> set = new HashSet<>();
@SuppressWarnings("rawtypes")
Set<TableRowMapper> instances = ClassSearcher.getInstances(TableRowMapper.class);
List<TableRowMapper> instances = ClassSearcher.getInstances(TableRowMapper.class);
for (TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE> mapper : instances) {
if (mapper.getSourceType() == fromType && mapper.getDestinationType() == toType) {
set.add(mapper);
@ -293,7 +293,7 @@ public class DiscoverableTableUtils {
List<ColumnConstraint<?>> mappedConstraints = new ArrayList<>();
@SuppressWarnings("rawtypes")
Set<ColumnTypeMapper> mappers = ClassSearcher.getInstances(ColumnTypeMapper.class);
List<ColumnTypeMapper> mappers = ClassSearcher.getInstances(ColumnTypeMapper.class);
for (ColumnTypeMapper<?, ?> mapper : mappers) {
mappedConstraints.addAll(generateMappedConstraints(mapper, foundConstraints));
@ -371,7 +371,7 @@ public class DiscoverableTableUtils {
private static List<ColumnConstraint<?>> findColumnConstraints() {
List<ColumnConstraint<?>> constraints = new ArrayList<>();
Set<ColumnConstraintProvider> constraintProviders =
List<ColumnConstraintProvider> constraintProviders =
ClassSearcher.getInstances(ColumnConstraintProvider.class);
for (ColumnConstraintProvider provider : constraintProviders) {

View file

@ -72,21 +72,43 @@ public class ClassFinder {
}
}
Set<Class<?>> getClasses(TaskMonitor monitor) throws CancelledException {
List<Class<?>> getClasses(TaskMonitor monitor) throws CancelledException {
Set<Class<?>> classes = new HashSet<>();
Set<Class<?>> classSet = new HashSet<>();
for (ClassDir dir : classDirs) {
monitor.checkCanceled();
dir.getClasses(classes, monitor);
dir.getClasses(classSet, monitor);
}
for (ClassJar jar : classJars) {
monitor.checkCanceled();
jar.getClasses(classes, monitor);
jar.getClasses(classSet, monitor);
}
return classes;
List<Class<?>> classList = new ArrayList<>(classSet);
Collections.sort(classList, (c1, c2) -> {
// Sort classes primarily by priority and secondarily by name
int p1 = ExtensionPointProperties.Util.getPriority(c1);
int p2 = ExtensionPointProperties.Util.getPriority(c2);
if (p1 > p2) {
return -1;
}
if (p1 < p2) {
return 1;
}
String n1 = c1.getName();
String n2 = c2.getName();
if (n1.equals(n2)) {
// Same priority and same package/class name....just arbitrarily choose one
return Integer.compare(c1.hashCode(), c2.hashCode());
}
return n1.compareTo(n2);
});
return classList;
}
/*package*/ static Class<?> loadExtensionPoint(String path, String fullName) {
@ -178,7 +200,7 @@ public class ClassFinder {
if (!Modifier.isPublic(c.getModifiers())) {
return false;
}
if (ExtensionPoint.Util.isExcluded(c)) {
if (ExtensionPointProperties.Util.isExcluded(c)) {
return false;
}

View file

@ -66,7 +66,7 @@ public class ClassSearcher {
static final Logger log = LogManager.getLogger(ClassSearcher.class);
private static ClassFinder searcher;
private static Set<Class<?>> extensionPoints;
private static List<Class<?>> extensionPoints;
private static WeakSet<ChangeListener> listenerList =
WeakDataStructureFactory.createCopyOnReadWeakSet();
@ -82,53 +82,67 @@ public class ClassSearcher {
}
/**
* Get classes that implement or derive from the given class
* Get {@link ExtensionPointProperties#priority() priority-sorted} classes that implement or
* derive from the given class
*
* @param c class filter class
* @param c the filter class
* @return set of classes that implement or extend T
*/
public static <T> Set<Class<? extends T>> getClasses(Class<T> c) {
public static <T> List<Class<? extends T>> getClasses(Class<T> c) {
return getClasses(c, null);
}
/**
* Get classes that implement or derive from the given class
* Get {@link ExtensionPointProperties#priority() priority-sorted} classes that
* implement or derive from the given class
*
* @param c class filter class
* @param c the filter class
* @param classFilter A Predicate that tests class objects (that are already of type T)
* for further filtering, <code>null</code> is equivalent to "return true"
* @return set of classes that implement or extend T and pass the filtering test performed
* by the predicate.
* @return {@link ExtensionPointProperties#priority() priority-sorted} list of
* classes that implement or extend T and pass the filtering test performed by the
* predicate
*/
@SuppressWarnings("unchecked") // we checked the type of each use so we know the casts are safe
public static <T> Set<Class<? extends T>> getClasses(Class<T> c,
public static <T> List<Class<? extends T>> getClasses(Class<T> c,
Predicate<Class<? extends T>> classFilter) {
if (isSearching) {
throw new IllegalStateException(
"Cannot call the getClasses() while the ClassSearcher is searching!");
}
Set<Class<? extends T>> set = new HashSet<>();
List<Class<? extends T>> list = new ArrayList<>();
if (extensionPoints == null) {
return set;
return list;
}
for (Class<?> extensionPoint : extensionPoints) {
if (c.isAssignableFrom(extensionPoint) &&
(classFilter == null || classFilter.test((Class<T>) extensionPoint))) {
set.add((Class<? extends T>) extensionPoint);
list.add((Class<? extends T>) extensionPoint);
}
}
return set;
return list;
}
public static <T> Set<T> getInstances(Class<T> c) {
public static <T> List<T> getInstances(Class<T> c) {
return getInstances(c, DO_NOTHING_FILTER);
}
public static <T> Set<T> getInstances(Class<T> c, ClassFilter filter) {
Set<Class<? extends T>> classes = getClasses(c);
Set<T> instances = new HashSet<>();
/**
* Get {@link ExtensionPointProperties#priority() priority-sorted} classes
* instances that implement or derive from the given class
*
* @param c the filter class
* @param filter A Predicate that tests class objects (that are already of type T)
* for further filtering, <code>null</code> is equivalent to "return true"
* @return {@link ExtensionPointProperties#priority() priority-sorted} list of
* classes instances that implement or extend T and pass the filtering test performed by
* the predicate
*/
public static <T> List<T> getInstances(Class<T> c, ClassFilter filter) {
List<Class<? extends T>> classes = getClasses(c);
List<T> instances = new ArrayList<>();
for (Class<? extends T> clazz : classes) {
if (!filter.accepts(clazz)) {
@ -298,7 +312,7 @@ public class ClassSearcher {
ResourceFile extensionClassesFile = new ResourceFile(appRoot, "EXTENSION_POINT_CLASSES");
try {
List<String> classNames = FileUtilities.getLines(extensionClassesFile);
Set<Class<?>> extensionClasses = new HashSet<>();
List<Class<?>> extensionClasses = new ArrayList<>();
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
@ -308,7 +322,7 @@ public class ClassSearcher {
Msg.warn(ClassSearcher.class, "Can't load extension point: " + className);
}
}
extensionPoints = Collections.unmodifiableSet(extensionClasses);
extensionPoints = Collections.unmodifiableList(extensionClasses);
}
catch (IOException e) {

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.
@ -16,11 +15,6 @@
*/
package ghidra.util.classfinder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* NOTE: ExtensionPoint logistics have changed! It is no longer sufficient to
* implement ExtensionPoint in order for the ClassSearcher to dynamically pick
@ -35,15 +29,5 @@ import java.lang.annotation.Target;
* will automatically search for and load.
*/
public interface ExtensionPoint {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface Exclude {
String reason();
}
public static class Util {
public static boolean isExcluded(Class<?> c) {
return c.getAnnotation(Exclude.class) != null;
}
}
// Marker interface
}

View file

@ -0,0 +1,83 @@
/* ###
* 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.util.classfinder;
import java.lang.annotation.*;
/**
* {@link ExtensionPoint} properties
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExtensionPointProperties {
/**
* Default {@link ExtensionPoint} priority. Higher values represent higher priorities.
*/
final static int DEFAULT_PRIORITY = 1;
/**
* Default behavior for an {@link ExtensionPoint} being discoverable
*/
final static boolean DEFAULT_EXCLUDE = false;
/**
* {@link ExtensionPoint} priority. Higher values represent higher priorities.
*
* @return the {@link ExtensionPoint} priority.
*/
int priority() default DEFAULT_PRIORITY;
/**
* Enable to exclude an {@link ExtensionPoint} from being discovered
*
* @return true to exclude an {@link ExtensionPoint} from being discovered
*/
boolean exclude() default DEFAULT_EXCLUDE;
/**
* Utility methods for working with {@link ExtensionPointProperties}
*/
public static class Util {
/**
* Gets whether or not the {@link ExtensionPoint} will be excluded from being discovered
*
* @param c the class check
* @return true if the class is an {@link ExtensionPoint} and should be excluded from being
* discovered
*/
public static boolean isExcluded(Class<?> c) {
ExtensionPointProperties properties = c.getAnnotation(ExtensionPointProperties.class);
return properties != null ? properties.exclude()
: ExtensionPointProperties.DEFAULT_EXCLUDE;
}
/**
* Gets the {@link ExtensionPoint} priority.
*
* @param c the class to get {@link ExtensionPoint} priority of.
* @return the class's {@link ExtensionPoint} priority
* ({@link ExtensionPointProperties#DEFAULT_PRIORITY} will be used in a
* non-{@link ExtensionPoint} is passed in)
*/
public static int getPriority(Class<?> c) {
ExtensionPointProperties properties = c.getAnnotation(ExtensionPointProperties.class);
return properties != null ? properties.priority()
: ExtensionPointProperties.DEFAULT_PRIORITY;
}
}
}

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.
@ -16,12 +15,6 @@
*/
package ghidra.framework.data;
import ghidra.framework.model.*;
import ghidra.framework.store.FileSystem;
import ghidra.framework.store.LockException;
import ghidra.util.Lock;
import ghidra.util.classfinder.ClassSearcher;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -29,6 +22,12 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import ghidra.framework.model.*;
import ghidra.framework.store.FileSystem;
import ghidra.framework.store.LockException;
import ghidra.util.Lock;
import ghidra.util.classfinder.ClassSearcher;
/**
* An abstract class that provides default behavior for
* DomainObject(s), specifically it handles listeners and
@ -488,7 +487,7 @@ public abstract class DomainObjectAdapter implements DomainObject {
contentHandlerClassMap = new HashMap<Class<?>, ContentHandler>();
contentHandlerTypeMap = new HashMap<String, ContentHandler>();
Set<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class);
List<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler ch : handlers) {
String type = ch.getContentType();
Class<?> DOClass = ch.getDomainObjectClass();

View file

@ -96,7 +96,7 @@ public class ProjectDataTableModel extends ThreadedTableModel<DomainFileInfo, Pr
@SuppressWarnings("rawtypes")
private List<ProjectDataColumn<?>> findAppSpecificColumns() {
Set<ProjectDataColumn> instances = ClassSearcher.getInstances(ProjectDataColumn.class);
List<ProjectDataColumn> instances = ClassSearcher.getInstances(ProjectDataColumn.class);
List<ProjectDataColumn<?>> columns = new ArrayList<>();
for (ProjectDataColumn projectDataColumn : instances) {

View file

@ -48,7 +48,7 @@ public class PluginClassManager {
(localExclusionClass == null || !localExclusionClass.isAssignableFrom(c)) &&
!ProgramaticUseOnly.class.isAssignableFrom(c);
Set<Class<? extends Plugin>> classes =
List<Class<? extends Plugin>> classes =
ClassSearcher.getClasses(Plugin.class, myClassFilter);
for (Class<? extends Plugin> pluginClass : classes) {

View file

@ -49,7 +49,8 @@ public abstract class PluginPackage implements ExtensionPoint, Comparable<Plugin
private static Map<String, PluginPackage> createPackageMap() {
Map<String, PluginPackage> map = new HashMap<>();
Set<Class<? extends PluginPackage>> classes = ClassSearcher.getClasses(PluginPackage.class);
List<Class<? extends PluginPackage>> classes =
ClassSearcher.getClasses(PluginPackage.class);
for (Class<? extends PluginPackage> class1 : classes) {
PluginPackage pluginPackage;
try {

View file

@ -119,7 +119,7 @@ public class PluginUtils {
// Now get all Plugin.class files that have been loaded, and see if any of them
// were loaded from one of the jars we just found.
Set<Class<? extends Plugin>> plugins = ClassSearcher.getClasses(Plugin.class);
List<Class<? extends Plugin>> plugins = ClassSearcher.getClasses(Plugin.class);
for (Class<? extends Plugin> plugin : plugins) {
URL location = plugin.getResource('/' + plugin.getName().replace('.', '/') + ".class");
if (location == null) {

View file

@ -390,7 +390,7 @@ class ToolServicesImpl implements ToolServices {
}
contentHandlers = new HashSet<>();
Set<ContentHandler> instances = ClassSearcher.getInstances(ContentHandler.class);
List<ContentHandler> instances = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler contentHandler : instances) {
// a bit of validation
String contentType = contentHandler.getContentType();

View file

@ -128,7 +128,7 @@ public class Handler extends URLStreamHandler {
private static void loadGhidraProtocolHandlers() {
protocolHandlers = new ArrayList<>();
Set<Class<? extends GhidraProtocolHandler>> classes =
List<Class<? extends GhidraProtocolHandler>> classes =
ClassSearcher.getClasses(GhidraProtocolHandler.class);
for (Class<?> c : classes) {
try {

View file

@ -16,7 +16,7 @@
package ghidra.program.model.data;
import java.util.ArrayList;
import java.util.Set;
import java.util.List;
import javax.swing.event.ChangeListener;
@ -134,7 +134,7 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
try {
ArrayList<DataType> list = new ArrayList<>();
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
Set<BuiltInDataType> datatypes =
List<BuiltInDataType> datatypes =
ClassSearcher.getInstances(BuiltInDataType.class, filter);
for (BuiltInDataType datatype : datatypes) {
list.clear();

View file

@ -110,7 +110,7 @@ public class SymbolUtilities {
private static List<String> getDynamicDataTypePrefixes() {
List<String> list = new ArrayList<>();
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
Set<BuiltInDataType> instances = ClassSearcher.getInstances(BuiltInDataType.class, filter);
List<BuiltInDataType> instances = ClassSearcher.getInstances(BuiltInDataType.class, filter);
for (BuiltInDataType builtIn : instances) {
String prefix = builtIn.getDefaultAbbreviatedLabelPrefix();
if (prefix != null) {

View file

@ -85,7 +85,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
}
private void searchForProviders() {
Set<LanguageProvider> languageProviders =
List<LanguageProvider> languageProviders =
ClassSearcher.getInstances(LanguageProvider.class);
searchCompleted = true;
@ -338,7 +338,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
throw new LanguageNotFoundException(processor);
}
private void processProviders(Set<LanguageProvider> providers) {
private void processProviders(List<LanguageProvider> providers) {
for (LanguageProvider provider : providers) {
addLanguages(provider);
}

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.
@ -16,18 +15,18 @@
*/
package ghidra.util.constraint;
import java.util.List;
import generic.constraint.DecisionTree;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import java.util.Set;
public class ProgramDecisionTree extends DecisionTree<Program> {
public ProgramDecisionTree() {
super();
Set<Class<? extends ProgramConstraint>> classes =
List<Class<? extends ProgramConstraint>> classes =
ClassSearcher.getClasses(ProgramConstraint.class);
for (Class<? extends ProgramConstraint> constraintClass : classes) {
try {

View file

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import generic.jar.ResourceFile;
import ghidra.framework.GModule;
import ghidra.util.SystemUtilities;
import utility.application.ApplicationLayout;
import utility.module.ModuleUtilities;
/**
@ -52,15 +53,17 @@ public class GhidraLauncher {
// Build the classpath
List<String> classpathList = new ArrayList<String>();
Map<String, GModule> modules = getOrderedModules(layout);
if (SystemUtilities.isInDevelopmentMode()) {
addModuleBinPaths(classpathList, layout.getModules());
addModuleBinPaths(classpathList, modules);
addExternalJarPaths(classpathList, layout.getApplicationRootDirs());
}
else {
addPatchPaths(classpathList, layout.getApplicationInstallationDir());
addModuleJarPaths(classpathList, layout.getModules());
addModuleJarPaths(classpathList, modules);
}
classpathList = orderClasspath(classpathList, layout.getModules());
classpathList = orderClasspath(classpathList, modules);
// Add the classpath to the class loader
GhidraClassLoader loader = (GhidraClassLoader) ClassLoader.getSystemClassLoader();
@ -189,6 +192,63 @@ public class GhidraLauncher {
return list;
}
/**
* Gets the modules ordered by "class-loader priority". This ensures that core modules (things
* in Framework/Features/Processors, etc) come before user modules (Extensions). It also
* guarantees a consistent module order from run to run.
*
* @param layout The layout
* @return the modules mapped by name, ordered by priority
*/
private static Map<String, GModule> getOrderedModules(ApplicationLayout layout) {
Comparator<GModule> comparator = (module1, module2) -> {
int nameComparison = module1.getName().compareTo(module2.getName());
// First handle modules that are external to the Ghidra installation.
// These should be put at the end of the list.
boolean external1 = ModuleUtilities.isExternalModule(module1, layout);
boolean external2 = ModuleUtilities.isExternalModule(module2, layout);
if (external1 && external2) {
return nameComparison;
}
if (external1) {
return -1;
}
if (external2) {
return 1;
}
// Now handle modules that are internal to the Ghidra installation.
// We will primarily order them by "type" and secondarily by name.
Map<String, Integer> typePriorityMap = Map.of(
"Framework", 0,
"Configurations", 1,
"Features", 2,
"Processors", 3,
"GPL", 4,
"Extensions", 5,
"Test", 6
);
String type1 = module1.getModuleRoot().getParentFile().getName();
String type2 = module2.getModuleRoot().getParentFile().getName();
int priority1 = typePriorityMap.getOrDefault(type1, typePriorityMap.size());
int priority2 = typePriorityMap.getOrDefault(type2, typePriorityMap.size());
if (priority1 != priority2) {
return Integer.compare(priority1, priority2);
}
return nameComparison;
};
List<GModule> moduleList = new ArrayList<>(layout.getModules().values());
Collections.sort(moduleList, comparator);
Map<String, GModule> moduleMap = new LinkedHashMap<>();
for (GModule module : moduleList) {
moduleMap.put(module.getName(), module);
}
return moduleMap;
}
/**
* Updates the list of paths to make sure the order is correct for any class-loading dependencies.
*
@ -199,18 +259,16 @@ public class GhidraLauncher {
private static List<String> orderClasspath(List<String> pathList,
Map<String, GModule> modules) {
//@formatter:off
Set<String> flatJars = modules
Set<String> fatJars = modules
.values()
.stream()
.flatMap(m -> m.getFatJars().stream())
.collect(Collectors.toSet());
//@formatter:on
List<String> orderedList = new ArrayList<String>(pathList);
for (String path : pathList) {
if (flatJars.contains(new File(path).getName())) {
if (fatJars.contains(new File(path).getName())) {
orderedList.remove(path);
orderedList.add(path);
}

View file

@ -26,6 +26,7 @@ import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import utilities.util.FileUtilities;
import utility.application.ApplicationLayout;
/**
* Utility methods for module related things.
@ -334,4 +335,21 @@ public class ModuleUtilities {
}
return findRepo(f.getParentFile());
}
/**
* Checks to see if the given {@link GModule module} is external to the Ghidra installation
* directory
*
* @param module the module to check
* @param layout Ghidra's layout
* @return true if the given {@link GModule module} is external to the Ghidra installation
* directory
*/
public static boolean isExternalModule(GModule module, ApplicationLayout layout) {
File moduleRootDir = module.getModuleRoot().getFile(false);
return !layout.getApplicationRootDirs()
.stream()
.map(dir -> dir.getParentFile().getFile(false))
.anyMatch(dir -> FileUtilities.isPathContainedWithin(dir, moduleRootDir));
}
}